How to unit test my composable parts with mocks

May 4, 2009 at 5:14 PM
Edited May 4, 2009 at 5:15 PM
I'm using Rhino Mocks; it's as good as any for my purposes.

What is the cleanest way to unit test my exports using dynamic mocks? I assume I still need to use the container in some capacity because the objects I'm testing have private imports that need to be satisfied by a composition container.

My assumption is that I need to:
1. Create a new container
2. Create a batch to include the required component(s) I'm testing
3. Add the export I'm testing
4. Add mocked objects to satisfy the export's import requirements
5. Add the batch to the container and then query for the export and run my tests.

The problem is in part #4 - how do I specify that a dynamic mock is to be exported under one or more contracts for the purpose of satiusfying my component's imports?
May 4, 2009 at 5:39 PM
Hi Nathan

There's a couple of different options here.

1. Using a batch, you can call the AddExportedObject method to add a mock instance to the container. AddExportedObject allows you to specify the contract for the instance that you are adding. i.e. batch.AddExportedObject(mockLogger, typeof(ILogger));

2. You an also create a custom export provider to allow you add mock instances. If you follow this link, here are some utils that I use. http://pastie.org/467842. Within you'll find a FakeExportProvider, along with FakeExportDefinitions. The FakeExportDefinitions take a func for the instance. This means you can pass it an instance, or even directly create a mock.

Here's sample code to illustrate usage.

        protected override void Context()
        {
            MockCache = MockRepository.GenerateStub<ICache>();
            var metadata = new Dictionary<string, object> ()
            var cacheDefinition = new FakeInstanceExportDefinition(typeof(ICache), MockCache, metadata);
            FakeProvider = new FakeExportProvider(f => ((FakeInstanceExportDefinition)f).Instance);
            FakeProvider.AddExportDefinitions(cacheDefinition);
            CacheExport = FakeProvider.GetExport<ICache>();
        }

Now above I am querying the export provider directly. However, our container allows passing in an export provider in it's construction. So you can do this...

var container = new CompositionContainer(null, FakeProvider).

HTH
Glenn

May 4, 2009 at 5:45 PM
Great, thanks!

I have avoided unit testing for ages but now that I've finally forced myself to get into it, it's good to have a quick response to prevent me from losing motivation on the testing.
May 4, 2009 at 5:59 PM
Edited May 4, 2009 at 5:59 PM
Nathan

Optimally you wouldn't need to use the container in your unit tests, as you should have a minimal set of dependencies. I understand you are doing that because you are using private imports.

Another option which would alleviate needing the container in the tests would be to use constructor injection over private imports. This way you can pass the mocks in directly on the constructor in a unit test.

For example below I have a part that accepts a mock logger in it's constructor.

[Export]
public class PartBeingTested {
  [ImportingConstructor]  
  public PartBeingTested(ILogger logger) {
    ...
  }
}

[Test]
public void SomeTest()  {
  var mockLogger == MockRepository.GenerateStub<ILogger>();
  var partBeingTested = new PartBeingTested(mockLogger);
  //act / assert
}

The one caveat of this is if the part needs to be recomposable, as constructor params cannot be recomposed. If you do need recomposition, you can either have them as public properties (if you want to set them in a unit test), or I have a post on a work-around here. http://codebetter.com/blogs/glenn.block/archive/2009/03/21/recomposition-and-constructors.aspx