c# - 使用 Moq 和 Autofac 进行单元测试

标签 c# unit-testing moq autofac

我有以下记录器记录器类,我想知道如何对其进行单元测试。

一些观察:

  1. 我需要创建接口(interface) IFileWrapper 以打破对 System.IO 的依赖,并能够进行用户依赖注入(inject) (Autofac)
  2. 我能够通过使用 MemoryString 实现 IFileWrapper 来对方法 FileWrapper.WriteLog 进行单元测试,但是如果我想测试方法内部的预期行为,我将无法(例如:抛出异常、不正确的路径和文件名等)

    /// <summary>
    /// Creates an instance of type <see cref="FileLogger"/>
    /// </summary>
    /// <remarks>Implements the Singleton Pattern</remarks>
    private FileLogger()
    {
        FileName = string.Format("\\{0: MMM dd, yy}.log", DateTime.Now);
        Path = Environment.CurrentDirectory;
    
        FileWrapper = ContainerBuilderFactory.Container.Resolve<IFileWrapper>();
    }
    
    /// <summary>
    /// Log the <paramref name="Message"/> in the <paramref name="Path"/> specified.
    /// The <paramref name="UserName"/>, <paramref name="Host"/> must be supplied
    /// </summary>
    /// <example>
    ///     <code>
    ///         var handler = new LoggerHandlerFactory();
    ///         var logger = handler.GetHandler<FileLogger>();
    ///         logger.Log("Hello CSharpLogger");
    ///     </code>
    /// </example>
    /// <exception cref="ArgumentNullException"></exception>
    /// <exception cref="ArgumentException"></exception>
    /// <exception cref="NotSupportedException"></exception>
    /// <exception cref="FileNotFoundException"></exception>
    /// <exception cref="IOException"></exception>
    /// <exception cref="SecurityException"></exception>
    /// <exception cref="DirectoryNotFoundException"></exception>
    /// <exception cref="UnauthorizedAccessException"></exception>
    /// <exception cref="PathTooLongException"></exception>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    /// <exception cref="FormatException"></exception>
    public void Log(string message, LogLevel level = LogLevel.INFO)
    {
        lock (_current)
        {
            var configLevel = CSharpLoggerConfiguration.Configuration.GetLogLevel();
    
            if (configLevel != LogLevel.OFF & level != LogLevel.OFF && configLevel >= level)
            {
                try
                {
                    FileWrapper.WriteLog(string.Concat(Path, FileName), message, level);
                }
                catch (CSharpLoggerException)
                {
                    throw;
                }
            }
        }
    }
    

因此,我使用 Moq 创建了以下单元测试:

 //arrange
        CSharpLoggerConfiguration.Configuration.SetLogLevel(LogLevel.DEBUG);

        var mock = new Mock<IFileWrapper>();
        mock.Setup(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()));

        logger.FileWrapper = mock.Object;

        //act
        logger.Log("Hello CSharpLogger", LogLevel.DEBUG);
        logger.Log("Hello CSharpLogger", LogLevel.WARN);

        //assert 
        mock.Verify(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()), Times.Exactly(2));

到目前为止一切顺利。我不满意的是这一行:logger.FileWrapper = mock.Object;我想将 FileWrapper 属性保密。

欢迎任何建议。

我将发布代码 http://csharplogger.codeplex.com/如果您需要更多详细信息。

最佳答案

使用constructor injection .简而言之;不是通过设置属性来提供服务(在本例中是文件包装器),而是使记录器具有一个公共(public)构造函数,该构造函数采用 IFileWrapper 参数。

public class Logger
{
    public Logger(IFileWrapper fileWrapper)
    {
        FileWrapper = fileWrapper;
    }

    public IFileWrapper FileWrapper { get; }
}

// in your test:
var logger = new Logger(mock.Object);

为了更彻底地回答关于拥有单例文件包装器的问题,下面是应用程序(非测试)代码的代码示例:

public static class FileWrapperFactory
{
    private static IFileWrapper _fileWrapper;

    public static IFileWrapper GetInstance()
    {
        return _fileWrapper ?? (_fileWrapper = CreateInstance());
    }

    private static IFileWrapper CreateInstance()
    {
        // do all the necessary setup here
        return new FileWrapper();
    }
}


public class StuffDoer
{
    public void DoStuff()
    {
        var logger = new FileLogger(FileWrapperFactory.GetInstance());

        logger.WriteLog("Starting to do stuff...");

        // do stuff

        logger.WriteLog("Stuff was done.");
    }
}

由于 FileWrapperFactory 维护文件包装器的静态实例,因此您永远不会拥有多个。但是,您可以像这样创建多个记录器,它们不必关心。如果您将来决定拥有多个文件包装器是可以的,则不必更改记录器代码。

在实际应用程序中,我建议您选择某种 DI 框架来为您处理所有这些簿记工作;大多数对单例实例都有很好的支持,基本上做上面的 FileWrapperFactory 所做的(但通常以更复杂和更健壮的方式。例如,FileWrapperFactory 不是线程安全的。 ..).

关于c# - 使用 Moq 和 Autofac 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39114925/

相关文章:

c# - CefSharp WinForms Web 浏览器不显示

c# - 使用基于约束的断言提高断言 IsNotNullOrEmpty 的可读性

.net - 如何使用 Moq 验证某个方法被调用了一次?

c# - 基于对同一模拟对象的早期 void 方法调用定义模拟响应

c# - 使用 JWT 检索 Azure AD 组信息

c# - 为方法声明设置多个返回值

python - 替换pytest中的测试用例继承?

angularjs - 如何对 ui-bootstrap $uibModal 实例的结果进行单元测试?

c# - 如何在 SignalR 2.x 单元测试框架中向模拟客户端添加组支持?

c# - 通过 TcpClient (byte[]) 发送包含特殊字符的字符串