Code

Using test stubs instead of mocks

Well, I’ll just come right out and say it. I’m not really a fan of mock libraries for unit test code. I think they’re the wrong kind of lazy.

As a programmer being lazy is good. Keep it in the back of your mind as you’re working. Not line-by-line thinking about how many keys you have to hit – more task-by-task thinking about what approach will become less of burden for you or your team in the long run.

So on the surface of it a library like Moq, NSubstitute, or FakeItEasy should save you time, right? You don’t need to make a new test stub class for each dependency. Or implement the entire interface. Or write code in the stubs to remember calls to assert on afterwards. That’s good, right?

Comparing a fake object to a stub class

I’m not sure that’s how it turns out in practice though. Consider the first-impressions example from https://fakeiteasy.github.io/.

// Creating a fake object is just dead easy!
// No mocks, no stubs, everything's a fake!
var lollipop = A.Fake<ICandy>();
var shop = A.Fake<ICandyShop>();

// Easily set up a call to return a value
A.CallTo(() => shop.GetTopSellingCandy()).Returns(lollipop);

// Use your fake as you would an instance of the faked type.
var developer = new SweetTooth();
developer.BuyTastiestCandy(shop);

// Asserting uses the same syntax as configuring calls.
// There's no need to learn another syntax.
A.CallTo(() => shop.BuyCandy(lollipop)).MustHaveHappened();

It’s a delightful API and a wonderful library. It does exactly what it proposes to accomplish. In just a few lines of code the SweetTooth component under test can make a call to a fake ICandyShop instance, and the fact that call has happened can be asserted.

The problem I think I have with that approach is that it encourages unit test methods to build up a large payload of transitive dependency details. Over time you should have more unit tests than components, so all of those test fixture details in your unit tests becomes a bit much. The last thing you want is for the number of tests you write to be inversely proportional to the effort it takes to refactor a component. After all, having more tests is supposed to give you the confidence to refactor aggressively – not penalize you for it.

By comparison, the above example as a test stub class would look like this

public class StubCandy : ICandy
{
    public string NameReturns {get; set;}

    string ICandy.Name => NameReturns;
}

public class StubCandyShop : ICandyShop
{
    // provide a default... why not...
    public StubCandy GetTopSellingCandyReturns {get; set;} = new StubCandy(); 

    // track calls... if there are multple parameters use a list of tuple...
    public List<ICandy> CallsToBuyCandy {get; set;} = new List<ICandy>(); 

    // implement interface explicitly so it's obvious which are incoming calls
    ICandy ICandyShop.GetTopSellingCandy() => GetTopSellingCandyReturns;
    void ICandyShop.BuyCandy(ICandy candy) => CallsToBuyCandy.Add(candy);
}

So, yeah, there’s an upfront overhead proportional to the number of members on your interface. But now let’s take a look at the test method

// Creating a stub object is okay!
// No mocks, no fakes, everything's a stub!
var lollipop = new StubCandy { Name = "lollipop" };
var shop = new StubCandyShop { GetTopSellingCandyReturns = lollipop };

// Use your stub as you would an instance of the stubbed type.
var developer = new SweetTooth();
developer.BuyTastiestCandy(shop);

// Assert using your favorite assert library.
Assert.AreEqual(lollipop, shop.CallsToBuyCandy.Single());

What did that do for us? Not much. 🙂 But I would argue that this test method will cost your team less over time. It’s kind of the same reason in Rails the idea is to have skinny controllers and put the smarts in the domain model.

Moving mechanics out of the test methods

For example, the specifics about the asserts can be moved into the stub class now.

public class StubCandyShop : ICandyShop
{
    //...
    public void AssertCallsToBuyCandy(params string[] names}
    {
        var candyNames = CallsToBuyCandy.Select(candy=>candy.Name);
        var union = candyNames.Union(names);
        var intersect = candyNames.Intersect(names);
        var difference = string.Join(", ", union.Except(intersect);
        Assert.AreEqual(union.Count(), intersect.Count(), 
            $"Expected and actual differ by {difference}");
    }
    //...
}

// And the test method has become...
// Arrange
var shop = new StubCandyShop 
{
    GetTopSellingCandyReturns = new StubCandy { Name = "lollipop" }
};

// Act
var developer = new SweetTooth();
developer.BuyTastiestCandy(shop);

// Assert
shop.AssertCallsToBuyCandy("lollipop");

Notice something about this? The test method itself is no longer tightly coupled to the details of the ICandyShop interface!

Instead – the test is coupled to the stub’s API to arrange preconditions, the class being tested of course, and the stub’s API to assert postconditions. Imagine you have several dozen test methods like this and you are considering changing the BuyCandy signature to take additional arguments like quantity, or changed GetTopSellingCandy so it returned an array of the top 5. In this example you could choose to keep the existing stub API intact for compatibility, and add a new Assert overload that takes name+quantity pairs.

The existing tests are still correct – but by concentrating the dependency details in a different location you’ve given yourself the opportunity to scale out the number of tests you write without having the cost of refactoring becoming directly proportional to the number of tests you’ve written.

And yes, you could move a lot of the fake and mock initialization code into a common location containing utility methods to act as object factories. I’ve done that before as well. I remember one day after I had just written a method which built and returned a mock object for an interface. I took a step back and though to myself, “I just mocked an object that implements an interface using lambda functions which close over local variables where they needed to share state — which is topologically identical to having a plain class implementing an interface with methods and fields — but five times harder for the next dev to figure out from looking at it.”

So I would submit that if you’re creating factory methods to manufacture mock interface implementations – that’s the signal you should be considering a stub class instead.

Streamlining per-test preconditions

Here’s a final trick I’ve used for the first time just a few weeks ago. It was a typical situation with an interface providing access to file-system assets and another providing ability to make REST calls. Same kind thing you’ll find over and over. That’s no problem of course, a dictionary of string to string is all you need for stub files, and url+method to status+body for REST calls, but the syntax for arranging the stub classes’ precondition information was becoming awfully cumbersome. There were a lot of curly braces nested four deep with multi-line string literals at the innermost level declaring the per-test file contents.

Then it occurred to me – I’m using a YAML deserialization library in this project already! And these stub classes are essentially nothing more than data-bearing POCO that implement an interface. I’ll just deserialize the stub and it’ll be completely populated with data and ready to use with a single line of code!

// Arrange
var shop = Deserialize<StubCandyShop>(@"
  GetTopSellingCandyReturns:
    NameReturns: lollipop
");

// Act
var developer = new SweetTooth();
developer.BuyTastiestCandy(shop);

// Assert
shop.AssertCallsToBuyCandy("lollipop");

Here’s another example of that from an actual unit test in a project I’m working on.

// Arrange
var blueprintManager = Deserialize<StubBlueprintManager>(@"
  Blueprints:
    the-test:
      Files:
        workflow.yaml: |
          operations:
          - message: Calling request but not producing output
            request: request.yaml
        request.yaml: |
          method: GET
          url: https://localhost/
");

var clientFactory = Deserialize<StubJsonHttpClientFactory>(@"
  Responses:
    https://localhost/:
      GET:
        status: 200
        body:
          beta:
            gamma: delta
");

InitializeServices(blueprintManager, clientFactory);

// Act
var result = Services.App.Execute("deploy", "the-test");

// Assert
// etc...

Nice, eh? Well, you get the idea, and this post has gone on long enough. In a nutshell I was happy how this turned out. I can create a large number of tests for different scenarios with self-contained preconditions quickly – when I change the blueprint manager or http client interfaces I only have one place to fix (the stubs themselves) – and there’s almost no actual code required to build up the preconditions.

Ah, and in case you were curious – the InitializeServices method takes a params object[] argument which is inspecting everything passed in for any interfaces they implement and registering them each as singleton services to that instance. So just the fact that this test has provided those two objects to InitializeServices has taken care of replacing them as dependencies as needed. So that’s another example of trying to be lazy in a good way. If I change the number and order of dependencies the App object takes in its constructor – well – I don’t need to go through and make fifty code changes in the unit tests because the component under test is not being new’ed up manually by the test method code.

One thought on “Using test stubs instead of mocks

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.