What are fakes, stubs and mocks?

Cesar Aguirre - Jun 7 '21 - - Dev Community

I originally posted an extended version of this post on my blog a couple of weeks ago. It's part of a series I've been publishing, called "Unit Testing 101"

Do you know what are fakes? Are stubs and mocks the same thing? Let's see what are the differences between them.

Fakes

The best analogy to understand fakes are flight simulators. With a flight simulator, teachers can recreate flight and environment conditions to train and test their pilot students in controlled scenarios.

Fakes are like flight simulators. Fakes are classes or components that replace external dependencies in your tests. With fakes, you can return values, throw exceptions or record method calls to test the code around it. Fakes create the conditions to test our code in controlled scenarios.

Two men sitting on aircraft control panel

Fakes are like "test" simulators. Photo by Jan Huber on Unsplash

Mocks vs Stubs

Now that we know what fakes are, let's see two types of fakes: mocks and stubs.

Fakes represent a broad category of test "simulators". Mocks and stubs are two types of fakes.

Stubs provide values or exceptions to the code under test and mocks are used to assert that a method was called with the right parameters.

OrderService example

To better understand the difference between mocks and stubs, let's use an example. Let's process online orders with an OrderService class.

This OrderService checks if an item has stock available to then charge a credit card. Imagine it uses an online payment processing software to take payments and an API request to a microservice to find the stock of an item. We use two interfaces, IPaymentGateway and IStockService, to represent the two dependencies. Something like this,

public class OrderService 
{
    private readonly IPaymentGateway _paymentGateway;
    private readonly IStockService _stockService;

    public OrderService(IPaymentGateway paymentGateway, IStockService stockService)
    {
        _paymentGateway = paymentGateway;
        _stockService = stockService;
    }

    public OrderResult PlaceOrder(Order order)
    {
        if (!_stockService.IsStockAvailable(order))
        {
            throw new OutOfStockException();
        }

        _paymentGateway.ProcessPayment(order);

        return new PlaceOrderResult(order);
    }
}
Enter fullscreen mode Exit fullscreen mode

To test the OrderService class, we should check two things. It should throw an exception if the purchased item doesn't have stock. And, it should take a payment if the purchased item has enough stock.

Let's write a test for the scenario of an item in stock.

Fake for inventory

First, we need a fake that returns stock available for any order. Let's call it: AlwaysAvailableStockService. It looks like this:

public class AlwaysAvailableStockService : IStockService
{
    public bool IsStockAvailable(Order order)
    {
        return true;
    }
}
Enter fullscreen mode Exit fullscreen mode

As its name implies, it will always return stock no matter the order we pass.

Fake for payment gateway

Second, the OrderService works if it charges a credit card. But, we don't want to charge a real credit card every time we run our test. That would be expensive!

Let's use a fake to record if the payment gateway was call or not. Let's call this fake: FakePaymentGateway. It looks like this:

public class FakePaymentGateway : IPaymentGateway
{
    public bool WasCalled;

    public void ProcessPayment(Order order)
    {
        WasCalled = true;
    }
}
Enter fullscreen mode Exit fullscreen mode

It has a public field WasCalled we set to true when the method ProcessPayment() is called.

Now that we have the AlwaysAvailableStockService and FakePaymentGateway in place, let's write the actual test.

[TestClass]
public class OrderServiceTests
{
    [TestMethod]
    public void PlaceOrder_ItemInStock_CallsPaymentGateway()
    {
        var paymentGateway = new FakePaymentGateway();
        var stockService = new AlwaysAvailableStockService();
        var service = new OrderService(paymentGateway, stockService);

        var order = new Order();
        service.PlaceOrder(order);

        Assert.IsTrue(paymentGateway.WasCalled);
    }
}
Enter fullscreen mode Exit fullscreen mode

The AlwaysAvailableStockService fake is there to provide an indirect input value for our test. It's an stub. And, the FakePaymentGateway is used to assert that the OrderService made a call to charge a credit card. It's a mock. Actually, we could call it MockPaymentGateway.

Voilà! Those are the difference between stubs and mocks. Again, fakes are like test "simulators". Stubs provides values and mocks are used to assert. Next time, you work with external dependencies in your tests, ask if you need a stub or a mock. They're not the same thing.


Upgrade your unit testing skills with my course: Mastering C# Unit Testing with Real-world Examples on Udemy. Practice with hands-on exercises and learn best practices by refactoring real-world unit tests.

Happy testing!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .