单元测试依赖项注入

时间:2019-06-10 06:19:46

标签: c# unit-testing dependency-injection inversion-of-control

我正在使用Autofac进行IoC

这是我的容器启动程序类,其职责是注册依赖项。

 public class ContainerInit
 {
      public static IContainer BuildContainer()
      {
            var conFac = new ContainerFactory();
            var builder = new ContainerBuilder();
            builder.Register(conFac).As<IContainerFactory>().SingleInstance();
            builder.Register(c=> new MainClass(conFac)).As<IMainClass>().SingleInstance();
            builder.Register(c=> new Database(conFac)).As<IDatabase>().SingleInstance();
             var logger = LoggUtil.CreateLogger();
            builder.Register(logger).As<ILogger>().SingleInstance();

            var container = builder.Build();
            ContainerFactory.SetContainer(container);
            return container;
      }
 }

这种方法的问题是,我需要将IContainerFactory传递给我在应用程序中使用的每个类的构造函数,如下所示:

  public class MainClass: IMainClass
  {       
      private readonly ILogger _logger;
      private readonly IDatabase _db;
      public MainClass(IContainerFactory containerFactory)
      {
              _logger = containerFactory.GetInstance<ILogger>();  
              _db =  containerFactory.GetInstance<IDatabase>(); //example       
      }
      public AddDetails(Data data)
      {
        //do some business operations 
        _db.Add(data);
        _logger.Information("added");
      }
  }

因此很难对这些类进行单元测试。

如何提出一个好的解决方案?

3 个答案:

答案 0 :(得分:2)

更好的方法是将类中需要的依赖项传递给构造函数:

public class MainClass : IMainClass
{       
    private readonly ILogger _logger;
    private readonly IDatabase _db;

    public MainClass(ILogger logger, IDatabase db)
    {
        _logger = logger;  
        _db = db;
    }

    public void AddDetails(Data data)
    {
        //do some business operations 
        _db.Add(data);
        _logger.Information("added");
    }
}

然后,您可以使用Moq之类的模拟框架来模拟您的类依赖关系,并验证是否调用了依赖关系:

[TestClass]
public class UnitTest1
{
    private Mock<ILogger> _mockLogger = new Mock<ILogger>();
    private Mock<IDatabase> _mockDb = new Mock<IDatabase>();

    [TestMethod]
    public void TestMethod1()
    {
        // arrange
        var mainClass = new MainClass(_mockLogger.Object, _mockDb.Object);
        var data = new Data();

        // act
        mainClass.AddDetails(data);

        // assert    
        _mockDb
            .Verify(v => v.Add(data), Times.Once);
    }
}

我不会验证您的日志消息,因为这可能会更改并使测试变脆。仅验证对实现该方法所必需的功能。

答案 1 :(得分:2)

您当前的服务定位器反模式使您的代码难以单独进行测试,并使类对其实际依赖的内容产生误导。

MainClass应该重构为遵循Explicit Dependencies Principle

public class MainClass : IMainClass  
    private readonly ILogger logger;
    private readonly IDatabase db;

    public MainClass(ILogger logger, IDatabase db) {
        this.logger = logger;  
        this.db = db;
    }

    public void AddDetails(Data data) {
        //do some business operations 
        db.Add(data);
        logger.Information("added");
    }
}

对于依赖于容器工厂的任何其他类,也应遵循相同的模式,例如Database

但是,您还需要相应地重构容器注册

public class ContainerInit {
    public static IContainer BuildContainer() {
        var builder = new ContainerBuilder();
        builder.RegisterType<MainClass>().As<IMainClass>().SingleInstance();
        builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
        var logger = LoggUtil.CreateLogger();
        builder.Register(logger).As<ILogger>().SingleInstance();

        var container = builder.Build();
        return container;
    }
}

测试MainClass仅要求您模拟被测类的必要依赖项。

[TestClass]
public class MainClassTests {    
    [TestMethod]
    public void Should_AddDetails_To_Database() {
        // Arrange
        var mockDb = new Mock<IDatabase>();
        var data = new Data();
        var mainClass = new MainClass(Mock.Of<ILogger>(), mockDb.Object);

        // Act
        mainClass.AddDetails(data);

        // Assert    
        mockDb.Verify(_ => _.Add(data), Times.Once);
    }
}

答案 2 :(得分:0)

在这里我想分享我在项目中使用的解决方案

要对特定功能进行单元测试,请使用以下结构

[TestClass]
public class TestSomeFunction
{
    public IComponentContext ComponentContext { get; set; }       

    [TestInitialize]
    public void Initialize()
    {
       //Registering all dependencies required for unit testing
       this.ComponentContext = builder.Build(); //You have not build your container in your question
    }

    [TestMethod]
    public void Testfunction()
    {
       //Resolve perticular dependency
       var _logger = containerFactory.Resolve<ILogger>();   
       //Test my function
       //use _logger 
    }
}