質問

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?

役に立ちましたか?

解決

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.

他のヒント

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.

Here's a more "philosophical" answer to your question :-)

My recommendation would be to not use the IOC container in your testing at all, if possible!

My rationale is that you need your test to have full control over the context of the test, and an IOC can take away some of this control. IMO, unit tests should be as focussed, small and predictable as possible!

Consider sending mock objects into your class under test, instead of actual classes.

If your class needs to have an internal instance of an IOC container, factor this out of the class into a "controller" of some sorts.

You could accomplish this in several ways, my favourite being using a framework like Rhino Mocks.

This way, you would actually stub out the "lifecycle" provided by the IOC at run time, in your test "setup".

So the test should have full control (through mocking and stubbing) over when objects are created or destroyed, using a framework like Rhino.

You could mock out the IOC, if this is even needed.

As a side note, one of the benefits of a well designed IOC container is that it should make unit testing easier - because it should discourage classes from relying on actual "concrete instances" of classes, and encourages the use of interchangeable interfaces instead.

You should try rely at run time on the IOC container providing the concrete implementations of the interfaces you've designed against.

Note that it's also normally important to get clarity about what you are actually testing. Unit tests should typically focus on testing the behavior of a single method on a single class.

If you're actually testing "more" than just one method on one class, for e.g. how a class interacts with other classes, it means you're most likely writing an "integration" test, not a true "unit" test.

Another note: I don't claim to be an expert at unit testing! They are incredibly useful, but I still struggle with testing more than almost any other aspect of coding.

For further reading, I highly recommend "The Art of Unit Testing" by Roy Osherove. There are others too.

The Art of Unit Testing

One question you should also ask yourself is: What do I actually want to test?

Your test suite shouldn't necessarily need to test 3rd party libs. In the above, membus was designed to deliver the messages to the handlers, tests in that library ensure that this is the case.

If you really want to test the message -> IOC handler delegation, in the case of Membus you could write a bus -> IOC adapter for testing:

public class TestAdapter : IocAdapter
{
    private readonly object[] _handlers;

    public TestAdapter(params object[] handlers)
    {
        _handlers = handlers;
    }

    public IEnumerable<object> GetAllInstances(Type desiredType)
    {
        return _handlers;
    }
}

And then use it with the instances under test.

        var bus =
            BusSetup.StartWith<Conservative>()
                    .Apply<IoCSupport>(
                        ioc => ioc.SetAdapter(new TestAdapter(new Handler<XYZ>()))
                    .SetHandlerInterface(typeof (IHandler<>)))
                    .Construct();

where you would keep the Handler instance around to do assertions against it. OTOH if you trust the library to do its work you would simply new up the handler and test its internal logic.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top