Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do i unit test a class that uses IoC container classes

I am using Autofac in my project,but i am unable to do a unit test on one particular class.

Consider the following Scenario :

//Class to be tested
public Class A
{
  private SomeAutoFacClass B;

  public void DoSomething()
  {
    B = scope.Resolve<ClassName>();// Resolve the object needed 
    // Do something with instance B
  }
}

// Test class
public Class ATest
{
  private A a;

  [test]
  public void TestMethod()
  {
    a.DoSomething();//*This method causes a null reference exception as it tries to resolve the objects*
  }
}

In the code above,i am not able to unit test case due to the dependency injection which is only specific to that particular class. how do i solve this? I also tried creating a autofaccontainer using Moq. But that too fails.

like image 220
vin Avatar asked Feb 05 '23 08:02

vin


1 Answers

The reason you are not able to test your class is because your class takes a dependency on your DI Container. This is an implementation of the Service Locator anti-pattern. It's an anti-pattern because:

the problem with Service Locator is that it hides a class' dependencies, causing run-time errors instead of compile-time errors, as well as making the code more difficult to maintain because it becomes unclear when you would be introducing a breaking change.

Instead, design your classes around

  • Constructor Injection in case the class is a component (a class that contains the applications behavior), where you inject the dependencies that a class directly needs through the constructor
  • Method Injection when the class is a data-centric object like an entity, which means the dependency is supplied to a the public method of such class, where the consuming class only uses that dependency, but not stores it.

Components are built-up by your DI Container and are registered in your Composition Root, while data-centric objects are newed up in code outside the Composition Root. In that case you need to pass along a dependency to an already constructed object.

In case you build and test a component, your code would typically look as follows:

public class ComponentA
{
    private ClassName b;

    public ComponentA(ClassName b)
    {
        this.b = b;
    }

    public void DoSomething()
    {
        // Do something with instance B
    }
}

// Test class
public Class ATest
{
    [test]
    public void TestMethod()
    {
        // Arrange
        B b = new FakeB();

        var a = new ComponentA(b);

        // Act
        a.DoSomething();

        // Assert
        // Check whether be was invoked correctly.
    }
}

In case you build and test a data-centric object that requires a dependency for one of its operations, your code would typically look as follows:

public class EntityA
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void DoSomething(ClassName b)
    {
        // Do something with instance B
    }
}

// Test class
public Class ATest
{
    [test]
    public void TestMethod()
    {
        // Arrange
        B b = new FakeB();

        var a = new EntityA { Name = "Bert", Age = 56 };

        // Act
        a.DoSomething(b);

        // Assert
        // Check whether be was invoked correctly.
    }
}

So to answer your initial question:

How do i unit test a class that uses IoC container classes

You don't. Your application code should not depend on the DI Container, because that leads to all kinds of complications such as being hard to test.

like image 109
Steven Avatar answered Feb 06 '23 20:02

Steven