Reputation: 1258
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.
Upvotes: 4
Views: 5274
Reputation: 172616
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
Components are built-up by your DI Container and are registered in your Composition Root, while data-centric objects are new
ed 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.
Upvotes: 10
Reputation: 34673
Using an IoC container you should aim to use IoC. Normally this is either constructor injection or property injection depending on what your container can support for auto-injection.
A pattern I use for this I call "lazy property injection" where I constructor-inject my container to act as a registry, then use the lazy resolution on property use..
What it looks like:
private readonly IoCContainer _container = null;
private IMyService _myService = null;
public IMyService MyService
{
get { return _myService ?? (_myService = _container.Resolve<IMyService>()); }
set { _myService = value; }
}
public MyClass( IoCContainer container)
{
if (container == null)
throw new ArgumentNullException("container");
_container = container;
}
Now when you go to test a method of this class, your test initializes your MyService with a Mock. When running under test, I have the IoCContainer initialized to a Mock() that throws if any .Resolve<> calls are made. This catches scenarios where your code under test might be modified to use a new dependency that hasn't been mocked out.
The benefit of this approach is that often a class will have several single-purpose methods, and by extension need a number of dependencies. In applications like web requests where maybe only one method is called, needing one dependency, the dependency resolution only retrieves the dependencies from the container that are needed, not all of them. (I.e. if you use constructor injection with 8 dependencies, all 8 need to be resolved at runtime, even if only 1 will be used.) This also simplifies unit testing to only mock out what you know will be needed rather than everything.
Upvotes: -2