kudlatiger
kudlatiger

Reputation: 3278

Unit testing the dependency injection

I am using Autofac for IoC

Here is my container initiator class, which the responsibility is to register the dependencies.

 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;
      }
 }

Problem with this approach is, I need to pass IContainerFactory to the constructor of every class I use in my application as follow

  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");
      }
  }

So it is difficult to unit test these classes.

How can come up with a good solution?

Upvotes: 8

Views: 37036

Answers (3)

Nkosi
Nkosi

Reputation: 247018

Your current Service Locator Anti-Pattern is what makes your code difficult to test in isolation as well as makes the class misleading about what it actually depends on.

MainClass should be refactored to follow 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");
    }
}

The same pattern should also be followed for any other class you have that depends on the container factory, like Database.

You would however need to also refactor the container registration accordingly

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;
    }
}

Testing MainClass would required you to mock only the necessary dependencies of the class under test.

[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);
    }
}

Upvotes: 4

Chris
Chris

Reputation: 3293

A better approach would be to pass the dependencies you need in your class into your constructor:

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");
    }
}

Then you could use a mocking framework such as Moq to mock your class dependencies and perform verifications on whether the dependencies were called:

[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);
    }
}

I would not verify your log message though as this could change and make the test brittle. Only verify functionality which is essential to doing what the method is intended for.

Upvotes: 11

Prasad Telkikar
Prasad Telkikar

Reputation: 16049

Here I would like to share solution, which I use in my project

To do unit testing of particular function, I use below structure

[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 
    }
}

Upvotes: 1

Related Questions