I am trying to increase the unit test coverage of an application and ran into an issue with mocking the child class of a base class that is injected into the class I'm trying to test. I am trying to test the ActionController
class and specifically trying to mock the _childClassHandler.DoSomething(myString, payload)
call.
public class ActionHandler : IActionHandler
{
private readonly IAction _action;
public ActionHandler(IAction action)
{
_action = action;
}
public void DoSomething(string myString, string payload)
{
//do stuff//
}
}
public class ChildClassHandler : ActionHandler
{
public ChildClassHandler(IAction childClass) : base(childClass) { }
}
public class ActionController
{
private readonly IActionHandler _actionHandler;
private readonly IChildClassHandler _childClassHandler;
public ActionController(IActionHandler actionHandler)
{
_actionHandler = actionHandler;
_childClassHandler = UnityHelper.GetContainer().Resolve<ChildClassHandler>();
}
public HttpResponseMessage PostMessage(string myString, string payload)
{
//do validations im trying to unit test//
_childClassHandler.DoSomething(myString, payload);
//return stuff//
}
}
I came across this s/o question that seems similar but all the proposed solutions are refactors and the question isn't exactly the same so I want to make sure I exhaust all resources before refactoring. I also tried the solution from this s/o question since the classes do not have an interface, but did not work. So my question is, am I able to mock the child class of an interface that is injected into a class's controller? Or will that method always be static and to unit test this I need to refactor the code?
Please let me know if more information is needed.
EDIT:
Adding UnityHelper.GetContainer() method:
private static readonly Lazy<IUnityContainer> _container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
RegisterAppSettings(container);
container.LoadConfiguration();
return container;
});
/// <summary>
/// Entry point for instantiating the helper and capturing the IUnityContainer.
/// </summary>
/// <returns></returns>
public static IUnityContainer GetContainer()
{
return _container.Value;
}
As at least one of the links in the OP implies, this is't a good design, so the best long-term solution is to change the code so that it's easier to test. If code becomes easier to test, it also becomes easier to use.
If you can't change the code, then you'll have to work around the awkwardness. It should be possible, though, although it's been more than a decade since I worked with Unity specifically.
I assume that UnityHelper.GetContainer
configures the container with production services. Thus, when the ActionController
constructor calls UnityHelper.GetContainer().Resolve<ChildClassHandler>()
it receives the ChildClassHandler
configured for production use, and not an object you can replace with a Test Double.
As far as I recall, the Unity container is, however, always mutable, so you can try to override the container from within a test. C#-near pseudocode follows. Here I'm assuming Moq, but you should be able to modify the example to use another dynamic mock library, if preferred.
var container = UnityHelper.GetContainer();
var origCch = container.Resolve<ChildClassHandler>();
try
{
var cchTD = new Mock<ChildClassHandler>();
// Overwrite the ChildClassHandler registration
container.RegisterInstance(cchTD.Object);
var ahTD = new Mock<IActionHandler>();
var sut = new ActionController(ahTD.Object);
// Put the rest of the test code here...
}
finally
{
// Restore the original service
container.RegisterInstance(origCch);
}
You need to do this in a try/finally
block to ensure that the global state gets properly reset after each test, even if the test fails. Otherwise, you may have one failing test causing a chain reaction of other tests also failing.
Unless the modern version of Unity is thread-safe, you also need to disable your test framework's (default?) parallelization, because you're now changing global mutable state.
As mentioned in the beginning, this kind of design comes with a series of problems. That the tests are difficult to write is just a symptom that the System Under Test is rigid.
Whether or not something like this would work hinges on whether or not Unity uses a last-registration-wins strategy. According to my own book, it did when I wrote it, but that's more than a decade ago.