Development Blog

 Sunday, August 05, 2007
« Request for Code Review - ReSharper.Test... | Main | Dependency Injection, Type Mock, Windmil... »

Jacob posted about the AutoMockingContainer several months ago. At that time we didn't really use it, it was just kind of an implementation of an idea. Well, we've finally started using it in some side projects (Resharper.TestDrive for example), and I must say... wow. It is most definitely the way to instantiate your subject under test most of the time. Why?

  1. It decouples your tests from your constructors. This means that if you have multiple TestFixtures for a class and you want to add a new service to your constructor, you don't have to change a thing in your tests.
  2. It simplifies your tests. Things are just cleaner when you're not having to create all your mock services to inject into your subject under test.
  3. It helps reinforce good mock usage. The default mock strategy is dynamic mocks. You can override that if you want to, but most tests should (in my opinion) be written with dynamic mocks. Like Dave talks about you only really want to set actual expectations on zero or one mock at a time. Everything else should be more stub-like.

I've started to use a base class for all my tests. Let's take a look at ReSharper.TestDrive's test base class:

  public abstract class AutoMockingTests 
  {
    private MockRepository _mocks;
    private AutoMockingContainer _container;

    protected MockRepository Mocks
    {
      get { return _mocks; }
    }

    protected AutoMockingContainer Container
    {
      get { return _container; }
    }

    [SetUp]
    public void BaseSetup()
    {
      _mocks = new MockRepository();
      _container = new AutoMockingContainer(_mocks);
      _container.Initialize();
      Setup();
    }

    public abstract void Setup();

    public T Create<T>()
    {
      return _container.Create<T>();
    }

    public T Mock<T>() where T : class
    {
      return _container.Get<T>();
    }

    public void Provide<TService, TImplementation>()
    {
      _container.AddComponent(typeof(TImplementation).FullName, typeof(TService), typeof(TImplementation));
    }

    public void Provide<TService>(object instance)
    {
      _container.Kernel.AddComponentInstance(instance.GetType().FullName, typeof(TService), instance);
    }
  }

So what's a test look like with this base class? Let's borrow Dave's example.

  public class SearchPresenterTests : AutoMockingTests
  {
    private SearchPresenter _presenter;
    private SearchResultDTO _fakeResults;

    public override void Setup()
    {
      this._fakeResults = new SearchResultDTO();
      this._presenter = Create<SearchPresenter>();
    }

    [Test]
    public void Can_search_for_customers_by_number_of_orders()
    {
      using (_mocks.Record())
      {
        Expect
         .Call(Mock<ISearchService>().GetCustomersByOrderCount(42))
         .Return(this._fakeResults);
      }

      using (_mocks.Playback())
      {
        _presenter.SearchByOrderCount(42);
      }
    }

    [Test]
    public void Search_results_are_displayed_to_the_user()
    {
      using (_mocks.Record())
      {
        mockView.SearchResults = _fakeResults;
        SetupResult
         .For(Mock<ISearchService>().GetCustomersByOrderCount(42))
         .Return(_fakeResults);
      }

      using (_mocks.Playback())
      {
        presenter.SearchByOrderCount(42);
      }
    }
  }

Not bad eh? You can do some more complicated things too. Let's say all your presenters take a hub service called PresenterServices. Rather than mocking it and its child services and setting up expectations for each of the children you can just use the real one and do this:

      Provide<IPresenterService>(Create<PresenterService>());
      this._presenter = Create<SearchPresenter>();

Now you can refer to all your hub's child services with the Mock<T>() method.

Ok, so if you made it this far you probably want to check it out for yourself. Thanks to Ayende, the AMC it is now part of Rhino.Tools so you can check it out (svn co https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk rhino-tools)  and build it yourself or just grab the current trunk build with all the dependencies here. Hope Oren doesn't mind me building and linking this... ;)

Sunday, August 05, 2007 3:18:07 PM (Pacific Standard Time, UTC-08:00)
That's just lovely.

One thing I wonder about AMC is, specifically in model or presenter layer scenarios, it would be cool to have different configurations of what the AMC doles out. What I'm thinking is a lot of BDD-style tests are acceptance tests and unit tests. In one mode I want them to run as my test-driven breadcrumb trail, in another I want them to run as full on, go through the stack of _real_ classes acceptance/integration tests. The main use case for me would be segmenting out when I run each mode, e.g. running in "unit mode" during the cc build and running in "acceptance mode" on another pass.

I tossed the idea out to oren and a bunch of other rhino mockers. The consensus seemed to be "neat but how do you deal w/ expectations." thought you guys would have some, well, thoughts...
Sunday, August 05, 2007 3:37:14 PM (Pacific Standard Time, UTC-08:00)
Hm, that's an interesting idea. Regardless of what you do, this would only work with a subset of tests--the tests that were backed by a database or what have you with the same data you've originally set up in the mocks...

If you did have a set of tests this would work with... then, without modifying Rhino.Mocks you'd have to proxy it with an a version that you could just "turn off". You'd also have a similar proxy for the container that would either use the AMC or your real, initialized container.

I think that what it really boils down to is that integration tests and unit tests are different... and combining them into one "switchable" test is probably more awkward than its worth.

Aaron
Sunday, August 05, 2007 8:35:00 PM (Pacific Standard Time, UTC-08:00)
I don't mind at all, of course.
Great article.
Monday, August 06, 2007 8:23:30 AM (Pacific Standard Time, UTC-08:00)
Aaron, doesnt that lower the test's ability to be self explanatory? That's my only concern about the AMC.
Monday, August 06, 2007 9:19:50 AM (Pacific Standard Time, UTC-08:00)
Yeah, that was one of the things I was going to put in my cons list but I never got around to writing it. If you are concerned with it, you can still create instance variables in your tests and initialize them with Mock<T>(). Doing that is arguably more explanatory than the ctor way of writing tests because you're only making those services you're actually testing visible in the tests.
Aaron
Wednesday, August 29, 2007 3:01:08 AM (Pacific Standard Time, UTC-08:00)
Say I am using windsor already and got all my components loaded in our default windsor container.

Now I want to get Component X and mock out all the injected interfaces in the constructor using the AMC.

Do I need to add the existing Windsor container to the AMC ?

David
Wednesday, August 29, 2007 4:53:22 AM (Pacific Standard Time, UTC-08:00)
Hey David,

You don't need to add your windsor container to the amc. It is self
contained. The one thing you do need to do, is call Create with an
instantiable type. Create is not Resolve. It needs to know what you
are constructing (how would it know otherwise what to instantiate?).

Try:

IHubService service = Create<HubService>();
Aaron Jensen
Comments are closed.