Fluent mocking...

Well first off... where has this month gone??... it's been 2 weeks since I last blogged... I thought it was only a week!

At any rate, one of the things I've been working on lately is mocking out COM interfaces (using RhinoMocks of course) ... and I have to admit that my eyes start crossing after a while, at about the point where the mock repositories setup code begins dominating the test case... which happens all too often... here's a smaller example of what I'm talking about:

MockRepository repository = new MockRepository();

TaggedValue oneTaggedValue = repository.DynamicMock();
Expect.Call(oneTaggedValue.Value = "").Constraints(Is.Equal("ExpectedValue"));
Expect.Call(textAlignTaggedValue.Update()).Return(true);

TaggedValue anotherTaggedValue = repository.DynamicMock();
Expect.Call(anotherTaggedValue.Value = "").Constraints(Is.Equal("AnotherExpectedValue"));
Expect.Call(textAlignTaggedValue.Update()).Return(true);

Collection taggedValuesCollection = repository.DynamicMock();

Expect.Call(taggedValuesCollection.GetByName("")).Constraints(Is.Equal("OneTaggedValue"))
.Return(oneTaggedValue).Repeat.Any();

Expect.Call(taggedValuesCollection.GetByName("")).Constraints(Is.Equal("AnotherTaggedValue"))
.Return(anotherTaggedValue).Repeat.Any();

Element destinationElement = repository.DynamicMock();
Expect.Call(destinationElement.TaggedValues).Return(taggedValuesCollection).Repeat.Any();

repository.ReplayAll();

So in this case we have an "Element", which has a collection of "TaggedValues", and we have two tagged values "OneTaggedValue" and "AnotherTaggedValue", and for each we are updating their values, and then invoking "Update()" on them to flush the changes...

To help solidify that, here's an example of what we're expecting to happen to our element (Element, Collection and TaggedValue are all interfaces from a COM interop assembly, in case you were wondering):

    Element element;

...

TaggedValue oneTaggedValue = (TaggedValue)element.TaggedValues.GetByName("OneTaggedValue");
oneTaggedValue.Value = "ExpectedValue";
oneTaggedValue.Update();
TaggedValue anotherTaggedValue = (TaggedValue)element.TaggedValues.GetByName("AnotherTaggedValue");
anotherTaggedValue.Value = "AnotherExpectedValue";
anotherTaggedValue.Update();

Now normally in this case I take unit tests once I've got "green" and refactor the mocking code out a bit... so in the above case I'd probably end up with something like:

MockRepository repository = new MockRepository();

Collection taggedValuesCollection = repository.DynamicMock();
AddTaggedValueSetter(repository, taggedValuesCollection, "OneTaggedValue", "ExpectedValue");
AddTaggedValueSetter(repository, taggedValuesCollection, "AnotherTaggedValue", "AnotherExpectedValue");

Element destinationElement = repository.DynamicMock();
Expect.Call(destinationElement.TaggedValues).Return(taggedValuesCollection).Repeat.Any();

repository.ReplayAll();

Which works... generally quite well... but lately I've been moving away from that approach and instead have been refactoring the setup code into a fluent interface... which I've found to be a lot easier to maintain and extend (and tends to be a little more re-usable across multiple test fixtures...) So for example, this is what the setup code for the above mock element is now:

MockRepository repository = new MockRepository();

Element element =
Mock
.Element()
.HasTaggedValue("OneTaggedValue").ExpectSetValue("ExpectedValue")
.HasTaggedValue("AnotherTaggedValue").ExpectSetValue("AnotherExpectedValue")
.Complete(repository);

repository.ReplayAll();

I'm just skimming the surface of what I've been doing, but it might be food for thought for anyone working on similar projects... anyone else doing similar things?

Written on March 20, 2007