报告代码执行和设计模式

本文关键字:设计模式 执行 代码 报告 | 更新日期: 2023-09-27 18:03:28

首先,我想感谢你们所有人对Stack Overflow社区的持续贡献!我多年来一直是Stack Overflow的一员,并且比任何其他在线资源更依赖您的输入。尽管我尽可能地参与并回答成员的问题,但偶尔我发现自己陷入困境,需要帮助。

说到这里,我有一个不寻常的代码问题。我正在编写c#中的API库,需要能够从WPF/Windows窗体应用程序调用,但也可以从单元测试代码中调用。

问题是,我需要能够报告(在Excel中)当API从WPF/windows窗体应用程序中调用时,库的每个方法是否正确执行,以及其他一些元数据和可选的返回类型。

当代码在单元测试中被使用时,我并不真正关心报告,但我确实需要能够生成一个Assert来判断API调用是否正确执行。

例如,如果在单元测试中我们有一个Test Initialize部分,其中一个API调用可能是为测试方法创建一个域用户。另一个还可以创建域组,以便用户具有适当的组成员资格。

为了适应WPF/WinForms API的消耗,我已经重写了API中的每个函数以返回OperationStep类型,希望当所有API调用执行时,我将有一个可以写入CSV文件的IEnumerable<OperationStep>

所以问题是有没有更简单的方法来实现我目前所做的?考虑到API库包含数百个类似的方法,编写报告非常繁琐且耗时。示例如下:

OperationStep<PrincipalContext> createDomainConnectionStep = DomainContext.Current.GetPrincipalContext(settings.DomainInfo);
OperationStep<UserPrincipal> createDomainUserStep = DomainContext.Current.CreateUser(createDomainConnectionStep.Context, settings.TestAccountInfo.Username, settings.TestAccountInfo.Password);
OperationStep<GroupPrincipal> createDomainGroupStep = DomainContext.Current.CreateGroup(createDomainConnectionStep.Context, settings.TestAccountInfo.UserGrupName);

其中DomainContext是一个单例对象,其功能是连接到域控制器并创建用户、组,并将用户关联到组。

请注意,第二个和第三个方法调用都需要第一个方法的输出,因此需要在OperationResult对象中使用public T Context,如下所述。

OperationStep对象由以下属性组成,这些属性由IOperation接口继承,public T Context除外。

public class OperationStep<T> : IOperation
{
    /// <summary>
    /// Denotes the Logical Name of the current operation
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// Denotes the stage of execution of the current operation: Setup, Execution, Validation, Cleanup 
    /// </summary>
    public OperationStage Stage { get; set; }
    /// <summary>
    /// Denotes whether the test step completed properly or failed.
    /// </summary>
    public OperationResult Result { get; set; }
    /// <summary>
    /// Denotes the return type of the test method.
    /// </summary>
    public T Context { get; set; }
    /// <summary>
    /// Denotes any other relevant information about the test step
    /// </summary>
    public string Description { get; set; }
    /// <summary>
    /// If the test step result is failed, this should have the stack trace and the error message.
    /// </summary>
    public string Error { get; set; }
}

方法调用本身有点臃肿和乏味,但这里有一个示例。

public class DomainContext
{
    private static volatile DomainContext currentContext;
    private static object synchronizationToken = new object();
    /// <summary>
    /// default ctor.
    /// </summary>
    private DomainContext() { }
    /// <summary>
    /// Retrieves the Current DomainContext instance.
    /// </summary>
    public static DomainContext Current
    {
        get
        {
            if (currentContext == null)
            {
                lock (synchronizationToken)
                {
                    if (currentContext == null)
                    {
                        currentContext = new DomainContext();
                    }
                }
            }
            return currentContext;
        }
    }
    /// <summary>
    /// Establishes a connection to the domain.
    /// </summary>
    /// <param name="domainInfo"></param>
    /// <returns></returns>
    public OperationStep<PrincipalContext> GetPrincipalContext(DomainInfo domainInfo)
    {
        OperationStep<PrincipalContext> result = new OperationStep<PrincipalContext>();
        result.Name = "Establish Connection to Active Directory";
        result.Result = OperationResult.Success;
        result.Stage = OperationStage.Setup;
        result.Description = string.Format("Domain Name: {0}, Default Containter: {1}", domainInfo.FQDN, domainInfo.Container);
        try
        {
            ContextType contextType = this.GetContextType(domainInfo.DomainType);
            PrincipalContext principalContext;
            try
            {
                principalContext = new PrincipalContext(contextType, domainInfo.FQDN, domainInfo.Container);
            }
            catch
            {
                throw new Exception("Unable to establish connection to Active Directory with the specified connection options.");
            }
            if (principalContext != null)
            {
                bool authenticationResult = principalContext.ValidateCredentials(domainInfo.Username, domainInfo.Password);
                if (!authenticationResult)
                {
                    throw new Exception("Unable to authenticate domain admin user to Active Directory.");
                }
                result.Context = principalContext;
                result.Result = OperationResult.Success;
            }
        }
        catch(Exception ex)
        {
            result.Error = ex.Message;
            result.Result = OperationResult.Failure;
        }
        return result;
    }
}

当所有的方法调用已经执行理论上我应该有一个IEnumerable<IOperation>,在win形式的情况下,我可以写在一个csv文件(在MS Excel中查看)或在单元测试的情况下,我可以简单地省略额外的信息和忽略(除了连续执行的方法和T Context属性)。

报告代码执行和设计模式

如果我理解正确的话-所有OperationStep s在这里只是为了记录。那么为什么不启用简单的。net日志呢?在方便的地方记录需要的信息。可以使用TraceSourceDelimetedTraceListener写入csv文件。不仅如此。您可以将日志逻辑移动到Strategy类中,并在单元测试中覆盖其日志方法,以便您调用Assert方法而不是日志。