Mocking Frameworks: Is it Worth Paying for Them?

Mocking Frameworks: Is it Worth Paying for Them?

When your team embarks on the unit testing journey, one of the first things you’ll find yourselves doing is evaluating mocking frameworks.  If you’re brand new to unit testing, “mocking framework” might seem an exotic concept.  But as a consultant and development coach specializing in TDD, I’ve helped a lot of companies start unit testing.  Believe me when I tell you that you’ll be researching this almost immediately.

Mocking Frameworks: the Why

Let me explain how and why this will happen.  Your very first exposure to unit tests will be simple, beautiful, and completely free of the normal complexities of code.  You’ll probably build some kind of exemplar calculator:

public class Calculator
    public int Add(int x, int y)
        return x + y;

And then, hey, unit testing is easy!  You write this bit of code, and you see the thrilling green of your first passing test:

public class CalculatorTests
    public void Adding_10_And_15_Returns_25()
        var calculator = new Calculator();

        int result = calculator.Add(10, 15);

        Assert.AreEqual<int>(25, result);

From there, you continue practicing.  You probably build out the rest of the calculator operations, possibly including logarithms and memory storage and such.  It’s good practice, and it’s a good place to start your unit testing journey.

But then you get back to your own codebase and figure you’ll try it out on some real method.  This looks like a good place to start.  You can just test that this method returns an order with the ID properly set:

public Order BuildSimpleOrder(int orderId)
    var order = new Order() { Id = orderId };

    _logger.Log($"Processed order {orderId}");

    return order;

So you do that, and your test runner kinda hangs for a while before returning an angry red failure.  It turns out that the logger in this class actually both connects to a database and writes a file for some reason.  And neither of those things go well from within the unit test runner.

So the next thing you do is think, “Huh. What can I do with that pesky logger line that I don’t even really care about?”  There are a number of suboptimal solutions that you might ponder first.  But eventually, you’ll arrive at the good solution: mocking frameworks.

Mocking Frameworks Explained

Many years ago, in the early days of unit testing, you might have done something that you’re probably thinking of doing now (i.e., one of the aforementioned suboptimal solutions).  You might have created a manual “mock” of the logger.  Say the logger is an interface and the class in question took a reference to it via constructor injection.  You could write an implementation of the logger, MockLogger : ILogger, whose Log(string) method did nothing.

In your unit test setup, you’d inject the mock logger, set up the test, and make your assertion.  Everything would go as planned, and when the test execution hit the _logger.Log() call, it would harmlessly no-op.  Problem solved.

Wait, so why is this suboptimal?

Well, many years ago, it wasn’t.  People implemented their own mock objects.  And then they implemented more of them as they created more interfaces.  And then they maintained all of this fake code, dutifully updating it as the interfaces changed.  I have actually done this, and I can attest to the fact that it’s a real pain.

So people started to create mocking frameworks.  These allow you to do all of this wonderful faking for your unit tests, but without the burden of maintenance code.  Relatively speaking, it feels like magic.

Finding Mocking Frameworks

Enough of my old man programming stories and back to your current one.  You’ve hit your wall with the logger and you start googling for what to do, alighting on the idea of mocking frameworks.

Next up, you face the decision of which one.  You’ll research a few different ones and maybe ask a question on Stack Overflow or ask some of your peers.  As you learn more about them, you’re going to find two distinct flavors:

  • Free and open source.
  • Paid tools.

At least, that’s the distinction that you think emerges.  And don’t get me wrong.  That is absolutely a way to bucket your options.  But what you really see is that this monetary split belies an underlying implementation split, which is more subtle.  Here’s how that goes:

  • Simple mocking tools.
  • Full-featured isolation frameworks.

Why don’t testing newbies pick up on this distinction immediately?  Well, because it’s pretty difficult to grok, right out of the gate.  So let me explain it here.

Simple Mocking

Simple mocking tools take advantage of natural “seams” in your object-oriented code.  This means that you can mock whenever you’ve successfully implemented polymorphism.  For instance, take my toy C# example above.

Recall that I said, “Say the logger is an interface, and the class in question took a reference to it via constructor injection.”  This is polymorphic.  Your constructor is a method, and any method that takes a base class or interface as an argument can also take a derived class or implementer, respectively.

Simple mocking frameworks take advantage of this behind the scenes.  You can think of a mocking framework as a magic box that automatically generates inheritance and interface implementations while hiding the details from you.  So anywhere you can inject a polymorph, you can make use of a mocking framework.

But what if you can’t inject it?  Well, that’s where a full-featured isolation framework comes in.

Full-Featured Isolation

Let’s go back to the logging example, but with a twist.  Instead of a constructor-injected invocation of a class field, let’s do what people not used to unit testing seem to love doing.  Let’s make the logger a singleton:

public Order BuildSimpleOrder(int orderId)
    var order = new Order() { Id = orderId };

    Logger.Instance.Log($"Processed order {orderId}");

    return order;

Just to be clear, that’s now a static call to Logger.Instance.Log().  There’s no constructor injection, and there’s no polymorphism to be had on static methods or properties.  With traditional mocking, you need a crowbar to pry that thing out of the method.  (Okay, you actually just need significant rework of the containing class.)

That’s where an isolation framework comes in.  With an isolation framework, you can still mock this superfluous dependency and write your unit test relatively easily.  And that’s powerful.

It’s also not limited to your implementation decisions.  With this approach, you can mock pesky static APIs, such as file I/O in C#.

To Isolate or Not to Isolate

As I mentioned earlier, I’ve coached teams on clean code and TDD over the year, and I also do codebase assessments.  In both capacities, I would offer a general piece of operating advice: you should write loosely coupled, abstract code for which you rarely, if ever, need isolation.  In other words, when you have a lot of singletons and static state and the like, your code suffers from more problems than just resistance to unit testing.

I stand by that advice in principle and in general.  But here’s the thing.  Shops that are just starting to unit test have a lot of code, and they’ve never before had much motivation to write code in a testable way.  So giving them this advice out of the gate threatens to blow out the little spark of excitement they have for testing.  It’s like installing a linter or static analyzer that spits back 26,000 warnings and then telling them they shouldn’t ever allow warnings.  It’s overwhelming.

Let me summarize that succinctly.  The shops that are newest to unit testing actually need isolation the most (as opposed to pedantic advice).  For unit testing novices, isolation capabilities can mean the difference between success and failure.  And for advanced unit testers, it’s still pretty handy to have isolation capabilities at the endpoints of your system (as opposed to writing a bunch of shim methods over the File API, for instance)—just don’t rely on them to gloss over decisions to write untestable code.

Is It Worth Paying for Mocking Frameworks?  Yes.

Let’s answer the title question, then.  When you pay for a mocking framework, you’re generally paying at least for isolation capabilities (though, obviously, these frameworks will all have feature differences beyond just isolation).  So should you pay for isolation as you adopt unit testing?

Yes, absolutely.

Think of it this way.  Even across a modest team, you’re spending a lot of money in man-hours adopting unit testing as a practice.  You’ve probably laid out some money for training, besides.  And you’re not just deciding to adopt unit testing in a vacuum. Some gap has probably brought you to this point.  Your investment in unit testing, speaking conservatively, is probably at least five figures.  And it’s a fragile investment because the team runs the risk of backsliding or abandoning the practice.

Now, if you could spend a few hundred dollars per developer and make that investment much more likely to pay off, would it be worth it?  I don’t think you’d find a businessperson in the world who would say no.

Leave a reply

Required fields are marked (*)