Exposing Fixture Object mocks

A few weeks ago, I stumbled upon coworker’s tests that use Fixture Object pattern. It is a nice way to simplify tests by moving setup code and repetitive helper methods to their own class. However, one thing did not seem quite right to me: tests that used mock objects from the fixture object needed to call an extension method AsMock() to run asserts on them. Since I was told that this approach is taken from one of Mark Seemann’s Pluralsight courses, that’s where I went for an answer and found Advanced Unit Testing course.

The example of Fixture Object usage in lessons includes the following code:

var fixture = new BasketControllerFixture();
var sut = fixture.CreateSut();
var basket = fixture.Basket.WithSmallDiscount().Build();
var channelMock = fixture.channel.AsMock();

Notice that the fixture object exposes it’s dependencies as public fields. However, even when they are mocked, the original type of dependency is used. The mocking framework used in video lessons is Moq and it’s main class is Mock(T). What the AsMock() extension method does under the hood is simply convert an object back to type of Mock(T).

One of the reasons I don’t like it is that AsMock() is an extension method that affects all types. A few years ago, I switched my default mocking framework from Rhino Mocks to Moq. I like that Mock(T) in Moq encapsulates and hides all the setup and assert methods that you can run on a mock away from your code. In Rhino Mocks, however, all these methods are extension methods on type of object. That means you can call them on any object, but, of course, most of them would fail if it’s not a mock object. And then there’s also a huge intellisense suggestion list every time you want to call a method or access a property of an object. In my opinion, that’s a great deal of noise when you write code.

Another problem is type conversion back and forth:

//In the fixture:
public class TestFixture
{
    public IDependency dependency;

    public TestFixture()
    {
        // 1: we create and setup a mock
        var mock = new Mock<IDependency>();
        // 2: from mock type to interface type
        dependency = mock.Object;
        ...
    }
}
...
// 3: in test, we convert the dependency back to a Mock(T) type
var fixture = new TestFixture();
var dependencyMock = fixture.dependency.AsMock();
dependencyMock.Verify(x => x.Test(), Times.Exactly(1));

That only makes the code harder to understand without any apparent advantages. From what I’ve seen, most often we use the object to assert that some method was called on the mock. And if we would need it as a dependency for another object, the code should go into the fixture anyway. This means that from outside of the fixture object class, we always need a mock. We would always call AsMock(). I do not see any value in exposing an IDependency object, if we never use it.

What I ended up doing in our project’s tests is refactor these fixture objects to expose a Mock(T) object directly and then delete AsMock() extension method from out codebase. This had no impact on test results, but the code ended up easier to understand:

public class TestFixture
{
    public Mock<IDependency> dependencyMock;

    public TestFixture()
    {
        dependencyMock = new Mock<IDependency>();
        ...
    }
}
...
var fixture = new TestFixture();
fixture.dependencyMock.Verify(x => x.Test(), Times.Exactly(1));

Conclusion
Expose your mocks directly from the fixture object if possible. If there is no real need to complicate things by converting types and introducing extension methods, don’t do it.

That said, this might only apply when using a framework like Moq that has a dedicated type for all mocks. Maybe Mark Seemann used this approach to be consistent with all other mocking frameworks. In the video, he even named AsMock() extension method’s class MockEnvy, indicating that it is a kind of code smell. However, some people who watch these videos and try this approach will actually do it in a copy-paste manner, without really trying to simplify things.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s