Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing object whose lifetime scope is handled by IoC container

I am using Microsoft Unit Test and have the following:

public class AccountCommandHandlers :
    Handler<CreateAccountCommand>,
     Handler<CloseAccountCommand>
{
    public bool CreateAccountCommandWasCalled = false;
    public bool CloseAccountCommandWasCalled = false;

    public void Handle(CreateAccountCommand command)
    {
        CreateAccountCommandWasCalled = true;
    }

    public void Handle(CloseAccountCommand command)
    {
        CloseAccountCommandWasCalled = true;
    }
}

[TestMethod]
public void CanRaiseInternalHandlers()
{
    var iocContainer = SimpleInjectorWiringForMembus.Instance;
    iocContainer.Bootstrap(
        AppDomain.CurrentDomain.GetAssemblies());

    var membus = MembusWiring.Instance;
    membus.Bootstrap();

    membus.Bus.Publish(new CreateAccountCommand() { Id = 100 });
    membus.Bus.Publish(new CloseAccountCommand() { Id = 100 });
}

I am using an IoC container (Simple Injector) which handles the lifetime scope of objects. Membus wires up commands to command handlers, and resolves via the IoC container.

The above code runs and works and the command handlers set their local variables to true.

However, since Simple Injector handles the lifetime scope, I cant ask Simple Injector for an AccountCommandHandler object as it would return a new object with CreateAccountCommandWasCalled set to false.

Being new to Unit Testing what would be a more robust way to test other than setting CreateAccountCommandWasCalled as a static variable?

like image 961
morleyc Avatar asked Apr 22 '13 17:04

morleyc


2 Answers

As the others have already mentioned, you are actually running integration tests. This isn't a problem however. Integration tests are suited for testing your IoC settings and for making sure that the different parts of your application work together.

With an integration tests however, you shouldn't be using mock or stub objects. Mocks and stubs have their uses in unit testing. A unit test is all about testing the smallest possible part of your code. Within a unit test, you use mocks to control the behavior of all dependencies that your class has. I wrote a blog a year ago that gives an introduction to the differences between integration and unit tests and how to use mocking in your tests.

In your situation, I wouldn't use the IoC container with production configuration for setting up your unit tests. Instead, I would switch to manually creating your objects in your tests and using a mocking tool like Moq to control the dependencies.

But this is also something that can be automated. A great tool is AutoFixture. 'Fixture' refers to the baseline you need to run your tests. This could be some sample data, the mocks and stubs you need and other setup code.

Mark Seemann (the developer behind AutoFixture) wrote a nice blog a couple of weeks ago about using AutoFixture together with an IoC as an Auto-mocking Container. I would advice to use something like this to structure your unit tests.

like image 130
Wouter de Kort Avatar answered Sep 24 '22 08:09

Wouter de Kort


As Steven said in his comments, it sounds like you are writing an integration test, in which case it does make sense to use the IoC container.

Your test project should have its own Composition Root for the IoC configuration. You can configure your IoC container to return a mock object for AccountCommandHandlers. Your test, rather than check the boolean members, can instead check that the Handle(CreateCommand) was called at least one time. With Moq, it would look something like this:

mock.Verify(foo => foo.Handle(createAccountCommand), Times.AtLeastOnce());

If for whatever reason you can't use a mock framework with the IoC config, it would be easy to create your own mock class for this one test case.

like image 41
Facio Ratio Avatar answered Sep 22 '22 08:09

Facio Ratio