5 tips for better stubs and mocks in C#

Last time, we covered what fakes are in unit testing and the types of fakes. We wrote two tests for a OrderService to show the difference between stubs and mocks.

In case you missed it, fakes are like test “simulators”. They replace external dependencies with testable components. Stubs and mocks are two type of fakes. Stubs are “simulators” that provide values or exceptions. And, mocks are “simulators” that record method calls.

Before we start with the tips, let’s bring back the OrderService class from our last post.

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);
    }
}

In our last post, we chose certain names for our fakes like “AlwaysAvailableStockService” and “FixedDateClock”. Let’s see why those names and what to do and not to do when working with fakes.

TL;DR

  • Don’t assert on stubs
  • Keep one mock per test
  • Avoid logic inside your fakes
  • Make tests set their own values for fakes
  • Name your fakes properly

1. Don’t assert on stubs

Remember, stubs are there to provide values indirectly to our code under test. We make fake return a value or throw an exception.

Don’t write assertions for stubs. We don’t need them. Assert on the result of your tests or use mocks.

Please, don’t do this.

[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);
        // We don't need this assertion
        Assert.IsTrue(stockService.IsStockAvailable(order));
    }
}

In this test, notice the next assertion,

Assert.IsTrue(stockService.IsStockAvailable(order));

It’s redundant. It will never fail because we wrote the fake to always return true. We can get rid of it!

If you use a mocking library to write your fakes and if you forget to setup your stubs, you will get a NullReferenceException. Your code expects some values that the stubs didn’t provide. With that exception thrown, you will have a failing test.

If you write assertions for your stubs, you’re testing the mocking library, not your code.

2. Keep one mock per test

In the same spirit of keeping a single assertion per test, keep one mock per test. Have small and well-named tests.

Let’s say that in our OrderService, we need to log every request we made to charge a credit card and we add a ILogger<AccountService> to our service.

Please, don’t write tests with more that one mock. Like this one,

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

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

        Assert.IsTrue(paymentGateway.WasCalled);
        // Keep one mock per test
        Assert.IsTrue(logger.WasCalled);
    }
}

Don’t use multiple mocks per test. Write separate tests, instead.

Cockpit of Airbus A330-200
Stubs and mocks are like test "simulators". Photo by Andrés Dallimonti on Unsplash

3. Avoid logic inside your fakes

Write dumb fakes. Avoid complex logic inside your fakes. For example, don’t add flags to your stubs to return one value or another. Write separate fakes, instead.

Let’s test the OrderService with and without stock. Don’t write the two tests with a single FakeStockService that uses a flag to signal the two scenarios.

[TestClass]
public class OrderServiceTests
{
    [TestMethod]
    public void PlaceOrder_ItemInStock_CallsPaymentGateway()
    {
        var paymentGateway = new FakePaymentGateway();
        // Make the stock service have stock
        var stockService = new FakeStockService
        {
            ItemInStock = true
        };
        var service = new OrderService(paymentGateway, stockService);

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

        Assert.IsTrue(paymentGateway.WasCalled);
    }

    [TestMethod]
    public void PlaceOrder_ItemOutOfStock_ThrowsException()
    {
        var paymentGateway = new FakePaymentGateway();
        // Make the stock service have NO stock
        var stockService = new FakeStockService
        {
            ItemInStock = false
        };
        var service = new OrderService(paymentGateway, stockService);

        var order = new Order();

        Assert.ThrowsException<OutOfStockException>(() => service.PlaceOrder(order));
    }
}

Don’t do that. Instead, write two separate fakes and make each test use a different one. For example, we can name these two fakes: ItemInStockStockService and ItemOutOfStockStockService. Inside them, we return always true and false, respectively.

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

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

        Assert.IsTrue(paymentGateway.WasCalled);
    }

    [TestMethod]
    public void PlaceOrder_ItemOutOfStock_ThrowsException()
    {
        var paymentGateway = new FakePaymentGateway();
        var stockService = new ItemOutOfStockStockService();
        var service = new OrderService(paymentGateway, stockService);

        var order = new Order();
        Assert.ThrowsException<OutOfStockException>(() => service.PlaceOrder(order));
    }
}

Don’t worry about creating lots of fakes. Fakes are cheap. Any decent IDE can add the methods you need when implementing an interface or an abstract class.

4. Make tests set their own values for fakes

Avoid magic values in your stubs. Make the test pass their own values instead of having hard-coded values in your tests.

Let’s say that StockService returns the units available instead of a simple true or false. Check this test,

[TestMethod]
public void PlaceOrder_NotEnoughStock_ThrowsException()
{
    var paymentGateway = new FakePaymentGateway();
    var stockService = new FakeStockService();
    var service = new OrderService(paymentGateway, stockService);

    var order = new Order
    {
        Quantity = 2
    };
    Assert.ThrowsException<OutOfStockException>(() => service.PlaceOrder(order));
}

Why should it throw? Why is that Quantity = 2 there? Because, we buried somewhere in the FakeStockService not enough stock. Something like this,

public class FakeStockService : IStockService
{
    public int StockAvailable(Order order)
    {
        return 1;
    }
}

Instead, let the test set their own faked value.

[TestMethod]
public void PlaceOrder_NoEnoughStock_ThrowsException()
{
    var paymentGateway = new FakePaymentGateway();
    var stockService = new FakeStockService
    {
        UnitsAvailable = 1
    };
    var service = new OrderService(paymentGateway, stockService);

    var order = new Order
    {
        Quantity = 2
    };
    Assert.ThrowsException<OutOfStockException>(() => service.PlaceOrder(order));
}

It makes more sense! There’s only 1 unit available and we’re placing an order for 2 items. Make tests fake their own values.

5. Name your fakes properly

Again for our last tip, let’s talk about names. Naming is hard.

Name your stubs to indicate the value they return or the exception they throw.

We named our fake stock provider AlwaysAvailableStockService to show it always return stock available. It obvious from its name what it’s the return value.

When we needed two stock providers to test the OrderService without stock, we named our fakes: ItemInStockStockService and ItemOutOfStockStockService.

Also, do you remember why we named our fake FixedDateClock? No? You can tell it by its name. It returns a fixed date, the DateTime you pass to it.

Voilà! Those are five tips to write better stubs and mocks. Remember, write dumb fakes. Don’t put too much logic in them. Let the tests fake their own values.

If you want to start using a mocking library, read my post on how to write fakes with Moq.

If you’re new to unit testing, read Unit Testing 101, 4 common mistakes when writing your first tests and 4 test naming conventions.

Happy testing!

What are fakes in unit testing: mocks vs stubs

Do you know what are fakes? Are stubs and mocks the same thing? Do you know if you need any of them? Once I made the same questions. Let’s see what are fakes in unit testing.

In unit testing, fakes are classes or components that replace external dependencies. Fakes simulate successful or failed scenarios to test the logic around the real dependencies they replace. Fakes are also called test doubles.

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

Fakes are like flight simulators. Fakes return values, throw exceptions or record method calls to test the code around it. They create the conditions to test our code in controlled scenarios.

Female aerospace engineer conducts flight simulator
Fakes are like flight simulators. Photo by ThisisEngineering RAEng on Unsplash

An example of Fakes

Let’s move to an example. In our last post when we wrote tests that use DateTime.Now, we slightly covered the concept of fakes. In that post, we wrote a validator for credit cards. It looks something like this:

public class CreditCardValidator : AbstractValidator<CreditCard>
{
    public CreditCardValidator(ISystemClock systemClock)
    {
        // Rest of code here...
        var now = systemClock.Now;
    }
}

public interface ISystemClock
{
    System.DateTime Now { get; }
}

public class SystemClock : ISystemClock
{
    public DateTime Now
        => DateTime.Now;
}

We created a ISystemClock interface with a Now property to replace DateTime.Now inside our validator. Then, in the unit tests, instead of using SystemClock with the real date and time, we wrote a FixedDateClock class to always return the same date and time.

This is one of the tests where wrote that uses the FixedDateClock class.

[TestClass]
public class CreditCardValidationTests
{
    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        var when = new DateTime(2021, 01, 01);
        var clock = new FixedDateClock(when);
        var validator = new CreditCardValidator(clock);

        var request = new CreditCardBuilder()
                        .WithExpirationYear(when.AddYears(-1).Year)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }
}

Well, that FixedDateClock is a fake. It replaces the SystemClock holding the real date and time with a testable replacement. With that fake in place, we make our tests use any date and time we want instead of the real date and time.

To be more precise, the FixedDateClock is a stub. But, let’s find out about stubs and mocks.

What’s the difference between Mocks and Stubs

Now that we know what fakes are, let’s see two types of fakes: mocks and stubs. This is the difference between them.

Both mocks and stubs are fakes or test doubles. 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 another 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 and 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);
    }
}

To test the OrderService class, we should check two things:

  • It should throw an exception if the purchased item doesn’t have stock.
  • 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 available stock

First, we need a fake that returns if there’s 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;
    }
}

As its name implies, it will always return stock for any 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.

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;
    }
}

It has a public field WasCalled we set to true when the method ProcessPayment() is called. This way we can assert if the payment gateway was called.

Now that we have 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);
    }
}

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

Again, stubs provides values and mocks are used to assert.

When to use fakes in our tests? Use fakes when in your unit tests when you depend on external systems you don’t control. For example, third-party APIs and message queues. Assert the right call were made or the right messages were sent.

Notice, we used the UnitOfWork_Scenario_ExpectedResult naming convention in our test. For the expect result part, we used the keyword “Calls”. It shows we expect the OrderService to call a payment gateway to charge credit cards.

For more naming conventions, check 4 test naming conventions

Other types of fakes: dummies, stubs, spies and mocks

We learned about mocks and stubs. But, there are more types of fakes or doubles.

The book xUnit Patterns presents a broader category of fakes. It uses: dummies, stubs, spies and mocks. Let’s quickly go through them.

Dummies are used to respect the signature of methods and classes under test. A dummy is never called inside the code under test. A null value or a null object, like NullLogger, in a class constructor are dummies when we’re testing one of the class methods that doesn’t use that parameter.

Stubs feed our code under test with indirect input values. We use stubs when the real dependencies aren’t available in the test environment or when using one will have side effects. Like charging a credit card. For “xUnit Patterns” stubs are exactly the same as what we described earlier.

Spies are observation points added to the code under test. We use spies to check if the code under test called another component or not, and the parameters it used. According to “xUnit Patterns”, mocks we wrote earlier are actually spies.

Mocks are testable replacements that check if they were used correctly. We use mocks when we know in advanced the parameters the code under test will use. With mocks, we set the expected parameters to be used before calling the code under test. Then, we use a verification method in the mock itself to check if the mock was called with those exact same parameters.

Don’t get confused with all these terms. Let’s stick to the types of fakes presented in the book The Art of Unit Testing (My takeaways here). In there, there are only two types of fakes or test doubles: stubs and mocks. Everything else is a fake. Easier!

Voilà! That’s what fakes are in unit testing. Remember, stubs provide values for our tests and mocks assert that calls were made. That’s the difference between them.

Often, we use the terms fake, stubs and mocks interchangeably. And sometimes we use the term “mocking” to mean the replacement of external components with testable equivalents. But, we have seen there’s a distinction between all these terms.

You can use mocking libraries to create your fakes, either stubs or mocks. If you want to start with a mocking library, read my post on how to write fakes with Moq.

If you’re new to unit testing, read Unit Testing 101, 4 common mistakes when writing your first tests and 4 test naming conventions.

Happy testing!

How to write tests that use DateTime.Now

In our last post about using builders to create test data, we wrote a validator for expired credit cards. We used DateTime.Now all over the place. Let’s see how to write better unit tests that use the current time.

To write tests that use DateTime.Now, create a wrapper for DateTime.Now and use a fake or test double with a fixed date. As alternative, create a setter or an optional constructor to pass a reference date.

Let’s continue where we left off. Last time, in our post about the the Builder pattern, we wrote two tests to check if a credit card was expired. These are the tests we wrote that time.

using FluentValidation.TestHelper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace UsingBuilders
{
    [TestClass]
    public class CreditCardValidationTests
    {
        [TestMethod]
        public void CreditCard_ExpiredYear_ReturnsInvalid()
        {
            var validator = new CreditCardValidator();

            var creditCard = new CreditCardBuilder()
                            .WithExpirationYear(DateTime.Now.AddYears(-1).Year)
                            .Build();
            var result = validator.TestValidate(request);

            result.ShouldHaveAnyValidationError();
        }

        [TestMethod]
        public void CreditCard_ExpiredMonth_ReturnsInvalid()
        {
            var validator = new CreditCardValidator();

            var creditCard = new CreditCardBuilder()
                            .WithExpirationMonth(DateTime.Now.AddMonths(-1).Month)
                            .Build();
            var result = validator.TestValidate(request);

            result.ShouldHaveAnyValidationError();
        }
    }
}

These two tests rely on the current date and time. Every time you run tests that rely on the current date and time, you will have a different date and time. It means, you will have different test values and tests each time you run these tests.

We want our tests to be deterministic. We learned that from Unit Testing 101. Using DateTime.Now in our tests isn’t a good idea.

To replace the DateTime.Now in our tests, we need seams.

A seam is a place to introduce testable behavior in our code under test.

Let’s see two techniques from the books The Art of Unit Testing (My takeaways here) and 97 things every programmer should know to introduce seams in our code to replace DateTime.Now.

Two techniques to introduce seams in code are using interfaces to declare dependencies in the constructor of a class and an optional setter methods to plug in testable values.

A clock alarm
Photo by Sonja Langford on Unsplash

Use a fake or test double

To make our tests more reliable, we can create an abstraction for the current time and make our validator depend on it. Later, we can pass a fake or double with a hardcoded date in our tests.

Let’s create a ISystemClock interface and a default implementation. The ISystemClock will have a Now property for the current date and time.

public interface ISystemClock
{
    DateTime Now { get; }
}

public class SystemClock : ISystemClock
{
    public DateTime Now
        => DateTime.Now;
}

Our CreditCardValidator will receive in its constructor a reference to ISystemClock. Now, instead of using DateTime.Now in our validator, it will use the Now property from the clock.

public class CreditCardValidator : AbstractValidator<CreditCard>
{
    public CreditCardValidator(ISystemClock systemClock)
    {
        // Rest of code here...
        var now = systemClock.Now;
    }
}

Next, let’s create a testable clock and use it in our tests.

public class FixedDateClock : ISystemClock
{
    private readonly DateTime _when;

    public FixedDateClock(DateTime when)
    {
        _when = when;
    }

    public DateTime Now
        => _when;
}

Notice we named our fake clock FixedDateClock to show it returns the DateTime object you pass to it.

Our tests with the testable clock implementation will look like this.

[TestClass]
public class CreditCardValidationTests
{
    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        var when = new DateTime(2021, 01, 01);
        var clock = new FixedDateClock(when);
        // This time we're passing a clock implementation
        var validator = new CreditCardValidator(clock);

        var request = new CreditCardBuilder()
                        .WithExpirationYear(when.AddYears(-1).Year)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    [TestMethod]
    public void CreditCard_ExpiredMonth_ReturnsInvalid()
    {
        var when = new DateTime(2021, 01, 01);
        var clock = new FixedDateClock(when);
        var validator = new CreditCardValidator(clock);

        var request = new CreditCardBuilder()
                        .WithExpirationMonth(when.AddMonths(-1).Month)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }
}

With a testable clock in our tests, we replaced all the references to DateTime.Now with a fixed date in the past.

Create constants for test values

To make things cleaner, let’s refactor our tests. Let’s use a builder method and read-only fields for the fixed dates.

[TestClass]
public class CreditCardValidationTests
{
    private static readonly DateTime When = new DateTime(2021, 01, 01);
    private static readonly DateTime LastYear = When.AddYears(-1);
    private static readonly DateTime LastMonth = When.AddMonths(-1);

    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        // Notice the builder method here
        var validator = MakeValidator(When);

        var request = new CreditCardBuilder()
                        .WithExpirationYear(LastYear.Year)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    [TestMethod]
    public void CreditCard_ExpiredMonth_ReturnsInvalid()
    {
        var validator = MakeValidator(When);

        var request = new CreditCardBuilder()
                        .WithExpirationMonth(LastMonth.Month)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    private CreditCardValidator MakeValidator(DateTime when)
    {
        var clock = new FixedDateClock(when);
        var validator = new CreditCardValidator(clock);
        return validator;
    }
}

That’s how we can abstract the current date and time with an interface.

Use a parameter in a constructor

Now, let’s see the second alternative. To replace the interface from our first example, in the constructor we can pass a delegate returning a reference date. Like this:

public class CreditCardValidator : AbstractValidator<CreditCard>
{
    public CreditCardValidator(Func<DateTime> nowSelector)
    {
        // Rest of code here...
        var now = nowSelector();
    }
}

Or, even simpler we can pass a plain DateTime parameter. Like this:

public class CreditCardValidator : AbstractValidator<CreditCard>
{
    public CreditCardValidator(DateTime now)
    {
        // Rest of code here...
    }
}

Let’s stick to a simple parameter and update our tests.

[TestClass]
public class CreditCardValidationTests
{
    private static readonly DateTime When = new DateTime(2021, 01, 01);
    private static readonly DateTime LastYear = When.AddYears(-1);
    private static readonly DateTime LastMonth= When.AddMonths(-1);

    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        var validator = new CreditCardValidator(When);

        var request = new CreditCardBuilder()
                        .WithExpirationYear(LastYear.Year)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    [TestMethod]
    public void CreditCard_ExpiredMonth_ReturnsInvalid()
    {
        var validator = new CreditCardValidator(When);

        var request = new CreditCardBuilder()
                        .WithExpirationMonth(LastMonth.Month)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }
}

Yeap! As simple as that. Another variation on this theme is to create a setter inside the CreditCardValidator to pass an optional date. Inside the validator, we should check if the optional date is present to use DateTime.Now or not. Something like this.

[TestMethod]
public void CreditCard_ExpiredYear_ReturnsInvalid()
{
    var validator = new CreditCardValidator();
    validator.CurrentDateTime = When;

    var request = new CreditCardBuilder()
                    .WithExpirationYear(LastYear.Year)
                    .Build();
    var result = validator.TestValidate(request);

    result.ShouldHaveAnyValidationError();
}

Voilà! That’s how we can write more reliable tests that use the current date and time. You can either create an interface or pass a fixed date.

If you’re new to unit testing, read Unit Testing 101 and 4 test naming conventions. For more advanced tips on unit testing, check my posts on how to write good unit tests and how to write fakes with Moq.

Happy testing!

How to create test data with the Builder pattern

Last time, we learned how to write good unit tests by reducing noise inside our tests. One way to reduce noise is to use builder methods. In that post, we used a builder method to simplify the complex setup scenarios of our tests. Let’s use the Builder pattern to create test data for our unit tests.

With the Builder pattern, an object creates another object. A builder has methods to change some properties of an object and a method to return an object ready to use. The Builder pattern is useful to create input data inside unit tests.

Without Builders

To see the Builder pattern in action, let’s validate credit cards. We will use the FluentValidation library to create a validator class. We want to check if a credit card is expired or not. We can write these tests.

using FluentValidation.TestHelper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace UsingBuilders
{
    [TestClass]
    public class CreditCardValidationTests
    {
        [TestMethod]
        public void CreditCard_ExpiredYear_ReturnsInvalid()
        {
            var validator = new CreditCardValidator();

            var creditCard = new CreditCard
            {
                CardNumber = "4242424242424242",
                ExpirationYear = DateTime.Now.AddYears(-1).Year,
                ExpirationMonth = DateTime.Now.Month,
                Cvv = 123
            };
            var result = validator.TestValidate(creditCard);

            result.ShouldHaveAnyValidationError();
        }

        [TestMethod]
        public void CreditCard_ExpiredMonth_ReturnsInvalid()
        {
            var validator = new CreditCardValidator();

            var creditCard = new CreditCard
            {
                CardNumber = "4242424242424242",
                ExpirationYear = DateTime.Now.Year,
                ExpirationMonth = DateTime.Now.AddMonths(-1).Month,
                Cvv = 123
            };
            var result = validator.TestValidate(creditCard);

            result.ShouldHaveAnyValidationError();
        }
    }
}

In these tests, we used the TestValidate() and ShouldHaveAnyValidationError() methods from FluentValidation to write better assertions.

In each test, we created a CreditCard object and modified one single property for the given scenario. We had duplication and magic values when initializing the CreditCard object.

From Unit Testing 101, we learned our test should be deterministic. We shouldn’t rely on DateTime.Now on our tests, but let’s keep it for now.

Object mothers

In our tests, we should give enough details to our readers, but not too many details to make our tests noisy. We should keep the details at the right level.

In our previous tests, we only cared for the expiration year and month in each test. We can abstract the creation of the CreditCard objects to avoid repetition.

One alternative to abstract the creation of CreditCard objects is to use an object mother.

An object mother is a factory method or property holding a ready-to-use input object. Each test changes this object to match the scenario under test.

For our example, we can create a CreditCard property with valid defaults and tweak it inside each test.

Our tests with an object mother for credit cards will look like this. Notice the CreditCard property in our test class and how we update its values from test to test.

[TestClass]
public class CreditCardValidationTests
{
    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        var validator = new CreditCardValidator();

        // Instead of creating a new card object each time,
        // we rely on this new CreditCard property
        var request = CreditCard;
        request.ExpirationYear = DateTime.Now.AddYears(-1).Year;
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    [TestMethod]
    public void CreditCard_ExpiredMonth_ReturnsInvalid()
    {
        var validator = new CreditCardValidator();

        var request = CreditCard;
        request.ExpirationMonth = DateTime.Now.AddMonths(-1).Month;
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    // We have this new property to hold a valid credit card
    private CreditCard CreditCard
        => new CreditCard
        {
            CardNumber = "4242424242424242",
            ExpirationYear = DateTime.Now.Year,
            ExpirationMonth = DateTime.Now.Month,
            Cvv = 123
        };
}
Lego technic toy truck
Let's use the Builder pattern. Photo by Markus Spiske on Unsplash

Builders

Object mothers are fine if you don’t have lots of variations of the object being constructed. But, since this is a post on Builder pattern, let’s create a Builder for credit cards.

A Builder is a regular class with two types of methods: a Build() method and one or more WithX() methods.

The Build() method returns the object the builder builds.

The WithX() methods update one or more properties of the object being built. In this name, the X refers to the property the method changes.

These WithX() methods return a reference to the builder itself. This way, we can chain many WithX() methods one after the other. One for each parameter we want to change.

For our example, let’s create a CreditCardBuilder with three methods: WithExpirationYear(), WithExpirationMonth() and Build().

public class CreditCardBuilder
{
    private string _cardNumber;
    private int _expirationYear;
    private int _expirationMonth;
    private int _cvv;

    public CreditCardBuilder WithExpirationYear(int year)
    {
        _expirationYear = year;

        return this;
    }

    public CreditCardBuilder WithExpirationMonth(int month)
    {
        _expirationMonth = month;

        return this;
    }

    // Other WithX methods

    public CreditCard Build()
    {
        return new CreditCard
        {
            CardNumber = _cardNumber,
            ExpirationYear = _expirationYear,
            ExpirationMonth = _expirationMonth,
            Cvv = _cvv
        };
    }
}

In our builder, we have one field for each property of the CreditCard class. We can create as many WithX() methods as properties we need to use in our tests.

Initialize values inside Builders

To initialize the properties without corresponding WithX() methods, we can create a special WithTestValues() method to use valid defaults. Another option is to initialize all the fields on the builder directly.

Let’s stick to the safe defaults out-the-box for our example.

public class CreditCardBuilder
{
    private string _cardNumber = "4242424242424242";
    private int _expirationYear = DateTime.Now.Year;
    private int _expirationMonth = DateTime.Now.Month;
    private int _cvv = 123;

    // All WithX() methods

    public CreditCard Build()
    {
        return new CreditCard
        {
            CardNumber = _cardNumber,
            ExpirationYear = _expirationYear,
            ExpirationMonth = _expirationMonth,
            Cvv = _cvv
        };
    }
}

Now that we have a CreditCardBuilder, let’s update our two sample tests to use it. Notice, when we use the Builder pattern, the last method in the chain of calls is always the Build() method.

[TestClass]
public class CreditCardValidationTests
{
    [TestMethod]
    public void CreditCard_ExpiredYear_ReturnsInvalid()
    {
        var validator = new CreditCardValidator();

        // Now, instead of creating cards with the new keyword and using an object mother,
        // we use a builder for credit cards
        var creditCard = new CreditCardBuilder()
                        .WithExpirationYear(DateTime.Now.AddYears(-1).Year)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }

    [TestMethod]
    public void CreditCard_ExpiredMonth_ReturnsInvalid()
    {
        var validator = new CreditCardValidator();

        var creditCard = new CreditCardBuilder()
                        .WithExpirationMonth(DateTime.Now.AddMonths(-1).Month)
                        .Build();
        var result = validator.TestValidate(request);

        result.ShouldHaveAnyValidationError();
    }
}

Composing Builders

With the Builder pattern, we can compose many builders to make our tests easier to read.

To show composition with builders, let’s book a room online. If we use an expired credit card when booking a room, our code will throw an exception. Let’s write a test for that.

[TestClass]
public class BookRoomTests
{
    [TestMethod]
    public void BookRoom_ExpiredCreditCard_ThrowsException()
    {
        var service = new BookingService();

        var request = new BookingRequestBuilder()
                        .WithGuest("John Doe")
                        .WithCreditCard(new CreditCardBuilder()
                                            .ExpiredCreditCard()
                                            .Build())
                        .Build();

        Assert.ThrowsException<InvalidCreditCardException>(() => service.BookRoom(request));
    }
}

Notice this time, we have a BookingRequestBuilder to create booking requests. This builder has two methods: WithGuest() and WithCreditCard(). Instead of creating credit cards directly, we used the CreditCardBuilder again. We created a new method ExpiredCreditCard() to build expired credit cards.

We can simplify even further our WithCreditCard() method to receive a credit card builder, not a credit card object. Like this.

[TestClass]
public class BookRoomTests
{
    [TestMethod]
    public void BookRoom_ExpiredCreditCard_ThrowsException()
    {
        var service = new BookingService();

        // Notice WithCreditCard() receives a builder this time
        var request = new BookingRequestBuilder()
                        .WithGuest("John Doe")
                        .WithCreditCard(new CreditCardBuilder()
                                            .ExpiredCreditCard())
                        .Build();

        Assert.ThrowsException<InvalidCreditCardException>(() => service.BookRoom(request));
    }
}

Voilà! That’s how we can use the Builder pattern to create test data for our unit tests. I hope you have more readable tests using the Builder pattern after reading this post. Remember, in your tests you should give enough details to your readers, but not too many to make your tests noisy.

We used DateTime.Now in our tests, let’s see how to handle that in a future post.

If you’re new to unit testing, read Unit Testing 101 to write your first unit tests in C# and learn how to name your test with these 4 naming conventions.

For more advanced tips on unit testing, check my post on how to write good unit tests and always write failing tests.

Happy testing!

How to name your unit tests. 4 test naming conventions

From our previous post, we learned about 4 common misatkes mistakes we make when writing our first unit tests. One of them is not to follow a naming convention. Let’s see four naming conventions for our unit tests.

Test names should tell the scenario under test and the expected results. Long names are acceptable when writing unit tests since test names show the purpose behind what they’re testing. Assertion messages aren’t needed if you name your test the right way.

These are 4 common naming conventions we can use. Let’s continue to use Stringie, a (fictional) library to manipulate strings. Stringie has a Remove() method to remove a substring from the beginning or the end of an input string.

1. UnitOfWork_Scenario_ExpectedResult

[TestClass]
public class RemoveTests
{
    [TestMethod]
    public void Remove_NoParameters_ReturnsEmpty() {}

    [TestMethod]
    public void Remove_ASubstring_RemovesOnlyASubstring() {}
}

We find this naming convention in the book The Art of Unit Testing (My takeaways here). This convention uses underscores to separate the unit of work or entry point, the test scenario and the expected behavior.

With this convention, we can read our test names out loud like this: “When calling Remove with no parameters, then it returns empty”.

2. Plain English sentence

[TestClass]
public class RemoveTests
{
    [TestMethod]
    public void Returns_empty_with_no_parameters() {}

    [TestMethod]
    public void Removes_only_a_substring() {}
}

Unlike the “UnitOfWork_Scenario_ExpectedResult” convention, this convention strives for a less rigid name structure.

This convention uses sentences in plain English for test names. We describe in a sentence what we’re testing in a language easy to understand even for non-programmers. For more readability, we separate in each word in our sentence with underscores.

This convention considers smells adding method names and filler words like “should” or “should be” in our test names. For example, instead of writing, “should_remove_only_a_substring”, we should write “removes_only_a_substring”.

You could read more about this convention in You are naming your tests wrong!

jar of blueberries
Photo by Debby Hudson on Unsplash

3. Sentence from classes and methods names

[TestClass]
public class RemoveGivenASubstring
{
    [TestMethod]
    public void RemovesThatSubstring() {}

    [TestMethod]
    public void RemovesThatSubstringFromTheEnd() {}
}

This naming convention uses sentences in plain English too. In this case, class names will act as the subject of our sentences and method names as the verb and the complement. We write units of work or entry points in class names and expected results in method names.

Also, we can split different scenarios in separate classes. We add the scenarios in class names with the work Given followed by the scenario under test.

For our Remove() method, we can name our test class RemoveGivenASubstring and our test methods RemovesOnlyASubstring and RemovesSubstringFromTheEnd.

With this convention, we can read our test names like full sentences in the “Test Explorer” menu in Visual Studio when we group our tests by class. Like this: “Remove, given a substring, removes that substring”.

Visual Studio Solution Explorer with our sample tests
Visual Studio 'Solution Explorer' showing our sample tests group by class

You can read more about this convention in ardalis’ Unit Test Naming Convention

4. Nested classes and methods

[TestClass]
public class RemoveTests
{
    [TestMethod]
    public void ReturnsEmpty() {}
    
    [TestClass]
    public class GivenASubstring
    {
        [TestMethod]
        public void RemovesThatSubstring() {}

        [TestMethod]
        public void RemovesThatSubstringFromTheEnd() {}
    }
}

This last convention uses sentences splitted into class and method names too. Unlike the previous naming convention, each scenario has its own nested class.

For example, instead of having a test class RemoveGivenASubstring, we create a nested class GivenASubstring inside a RemoveTests class.

You can learn more about this last convention in Kevlin Henney’s presentation Structure and Interpretation of Test Cases.

Voilà! That’s how we can name our unit tests. Remember naming things is hard. Pick one of these four naming convention and stick to it. But, if you inherit a codebase, prefer the convention already in used. I hope you can write more readable test names after reading this post.

If you want to practice naming unit tests, check my Unit Testing 101 repository. There you will find the test names that Stringie developers wrote. Your mission, Jim, should you choose to accept it, is to write better names.

canro91/Testing101 - GitHub

If you’re new to unit testing, read my post on how to write your first unit tests in C# and check the 4 common mistakes when writing your first tests.

Happy testing!