The Rhino.Mocks’ AssertWasCalled method does work


This behavior is probably clearly specified somewhere, but somehow it has been non-obvious to the four people on our team to the point that we were leaning toward banning its use. I didn’t want to go that far, so figured I better figure out the rules.

I’m referring to the AssertWasCalled extension method in Rhino.Mocks that allows the Arrange/Act/Assert style of testing. We found that sometimes it would behave how we expected, and sometimes it would not, with no apparent explanation. I finally realized that it probably had something to do with how the mock objects were created, so I wrote this simple test:

   1: [TestFixture]
   2: public class AssertCalledTester
   3: {
   4:     private Dictionary<string, IDependency> dependencies;
   5:     private MockRepository mocks;
   7:     [SetUp]
   8:     public void Setup()
   9:     {
  10:         mocks = new MockRepository();
  11:         dependencies = new Dictionary<string, IDependency>()
  12:        {
  13:            {"GenerateMock", MockRepository.GenerateMock<IDependency>()},
  14:            {"GenerateStub", MockRepository.GenerateStub<IDependency>()},
  15:            {"StrictMock", this.mocks.StrictMock<IDependency>()},
  16:            {"DynamicMock", this.mocks.DynamicMock<IDependency>()},
  17:            {"Stub", this.mocks.Stub<IDependency>()},
  18:        };
  19:     }
  21:     [RowTest]
  22:     [Row("GenerateMock")]
  23:     [Row("GenerateStub")]
  24:     [Row("StrictMock")]
  25:     [Row("DynamicMock")]
  26:     [Row("Stub")]
  27:     public void When_does_it_work(string mockStyle)
  28:     {
  29:         var dependency = dependencies[mockStyle];
  30:         var caller = new Caller(dependency);
  31:         caller.Go();
  32:         dependency.AssertWasCalled(d => d.DoSomething());
  33:     }
  34: }
  36: public class Caller
  37: {
  38:     private readonly IDependency dependency;
  40:     public Caller(IDependency dependency)
  41:     {
  42:         this.dependency = dependency;
  43:     }
  45:     public void Go()
  46:     {
  47:         dependency.DoSomething();
  48:     }
  49: }
  51: public interface IDependency
  52: {
  53:     void DoSomething();
  54: }

The results (3 out of 5 tests fail) clearly illustrated the source of our problems: AssertWasCalled only works automatically if you create your mocks using the new static MockRepository methods. Ayende’s initial announcement of the feature used these static methods, but it wasn’t explicit that they were required. They just appeared to be convenience methods. However, reading into the comments, I see that Ayende posted on 6/14/08 that the static methods are indeed special:

The static methods are returning an object that can be used without record/replay. It is already in replay mode, which is needed to use the AAA syntax.

If aren’t using the static methods (or are using something like StructureMap’s RhinoAutoMocker where the mocks are created for you), you can still utilize AssertWasCalled as long as you call ReplayAll() before the “act” phase of your test. For example, in the code above I can add “mocks.ReplayAll()” before line 31 to get 4 out of the 5 tests to pass. The only one that fails is the one that uses a StrictMock, which isn’t too worrisome.

Running jQuery QUnit tests under Continuous Integration