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 exact same questions. Let’s see what are fakes in unit testing.
In unit testing, fakes or test doubles are classes or components that replace external dependencies. Fakes simulate successful or failed scenarios to test the logic around the real dependencies they replace.
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.
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:
publicclassCreditCardValidator:AbstractValidator<CreditCard>{publicCreditCardValidator(ISystemClocksystemClock){// Rest of code here...varnow=systemClock.Now;}}publicinterfaceISystemClock{System.DateTimeNow{get;}}publicclassSystemClock:ISystemClock{publicDateTimeNow=>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.
Well, that FixedDateClock is a fake. It replaces the SystemClock holding the real date and time with a testable alternative. 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,
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.
Let’s use fakes in our unit tests when we depend on external systems we don’t control. For example, third-party APIs and message queues. Let’s assert the right call were made or the right messages were sent.
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.
Let’s not get confused with all these terms. Let’s stick to the types of fakes presented in the book The Art of Unit Testing. In there, there are only two types of fakes or test doubles: stubs and mocks. Everything else is a fake. Easier!
Parting thoughts
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.
And don’t miss the rest of my Unit Testing 101 series where I cover more subjects like this one.
Want to write readable and maintainable unit tests in C#? Join my course Mastering C# Unit Testing with Real-world Examples on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
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 an alternative, create a setter or an optional constructor to pass a reference date.
Let’s continue where we left off. Last time, we wrote two tests to check if a credit card was expired using the Builder pattern. These are the tests we wrote at that time.
These two tests rely on the current date and time. Every time we run tests that rely on the current date and time, we will have a different date and time. It means we will have different test values and tests each time we 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.
What are seams?
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. Two techniques to introduce seams are interfaces to declare dependencies in the constructor of a service and optional setter methods to plug in testable values.
Let’s see these two techniques to replace the DateTime.Now in our tests.
1. Use a fake or test double to replace DateTime.Now
To make our tests more reliable, let’s create an abstraction for the current time and make our validator depend on it. Later, we can pass a fake or test double with a hardcoded date in our tests.
Let’s create an ISystemClock interface and a default implementation. The ISystemClock will have a Now property for the current date and time.
Our CreditCardValidator will receive in its constructor a reference to ISystemClock. Then, instead of using DateTime.Now in our validator, it will use the Now property from the clock.
publicclassCreditCardValidator:AbstractValidator<CreditCard>{publicCreditCardValidator(ISystemClocksystemClock){varnow=systemClock.Now;// Beep, beep, boop// Rest of the code here...}}
Next, let’s create a testable clock and use it in our tests.
Notice we named our fake clock FixedDateClock to show it returns the DateTime we pass to it.
Our tests with the testable clock implementation will look like this,
[TestClass]publicclassCreditCardValidationTests{[TestMethod]publicvoidCreditCard_ExpiredYear_ReturnsInvalid(){varwhen=newDateTime(2021,01,01);varclock=newFixedDateClock(when);// This time we're passing a fake clock implementationvarvalidator=newCreditCardValidator(clock);// ^^^^^varrequest=newCreditCardBuilder().WithExpirationYear(when.AddYears(-1).Year)// ^^^^.Build();varresult=validator.TestValidate(request);result.ShouldHaveAnyValidationError();}[TestMethod]publicvoidCreditCard_ExpiredMonth_ReturnsInvalid(){varwhen=newDateTime(2021,01,01);varclock=newFixedDateClock(when);varvalidator=newCreditCardValidator(clock);// ^^^^^varrequest=newCreditCardBuilder().WithExpirationMonth(when.AddMonths(-1).Month)// ^^^^.Build();varresult=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.
UPDATE (June 2024): .NET 8.0 introduced TimeProvider, a new abstraction to use and test time. With this new built-in abstraction, we don’t need to roll our own ISystemClock.
Create constants for common 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.
That’s how we can abstract the current date and time with an interface.
2. 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:
publicclassCreditCardValidator:AbstractValidator<CreditCard>{publicCreditCardValidator(Func<DateTime>nowSelector)// ^^^^^{varnow=nowSelector();// Beep, beep, boop// Rest of the code here...}}
Or, even simpler we can pass a plain DateTime parameter. Like this:
publicclassCreditCardValidator:AbstractValidator<CreditCard>{publicCreditCardValidator(DateTimenow)// ^^^^^{// Beep, beep, boop// Rest of the code here...}}
Let’s stick to a simple parameter and update our tests.
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,
And don’t miss the rest of my Unit Testing 101 series where I cover more subjects like this one.
Want to write readable and maintainable unit tests in C#? Join my course Mastering C# Unit Testing with Real-world Examples on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
Last time, we learned how to write good unit tests by reducing noise inside our tests. We used a factory method to simplify complex setup scenarios in 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 the state of an object and a Build() method to return that object ready to use. Often, the Builder pattern is used to create input data inside unit tests.
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,
In these tests, we used the TestValidate() and ShouldHaveAnyValidationError() helper methods from FluentValidation to write more readable 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.
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 about a credit card expiration year and month. 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 the properties of an object mother to match the scenario under test.
For our example, let’s 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,
[TestClass]publicclassCreditCardValidationTests{[TestMethod]publicvoidCreditCard_ExpiredYear_ReturnsInvalid(){varvalidator=newCreditCardValidator();varrequest=CreditCard;// ^^^^^// Instead of creating a new card object each time,// we rely on this new CreditCard propertyrequest.ExpirationYear=DateTime.Now.AddYears(-1).Year;varresult=validator.TestValidate(request);result.ShouldHaveAnyValidationError();}[TestMethod]publicvoidCreditCard_ExpiredMonth_ReturnsInvalid(){varvalidator=newCreditCardValidator();varrequest=CreditCard;// ^^^^^request.ExpirationMonth=DateTime.Now.AddMonths(-1).Month;varresult=validator.TestValidate(request);result.ShouldHaveAnyValidationError();}// We have this new property to hold a valid credit cardprivateCreditCardCreditCard// ^^^^^=>newCreditCard{CardNumber="4242424242424242",ExpirationYear=DateTime.Now.Year,ExpirationMonth=DateTime.Now.Month,Cvv=123};}
Notice the CreditCard property in our test class and how we updated its values from test to test.
Object mothers are fine if we 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 chainable 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 property we want to change.
For our example, let’s create a CreditCardBuilder with three methods: WithExpirationYear(), WithExpirationMonth(), and Build().
publicclassCreditCardBuilder{privatestring_cardNumber;privateint_expirationYear;privateint_expirationMonth;privateint_cvv;publicCreditCardBuilderWithExpirationYear(intyear){_expirationYear=year;returnthis;}publicCreditCardBuilderWithExpirationMonth(intmonth){_expirationMonth=month;returnthis;}// Other WithX() methods...publicCreditCardBuild(){returnnewCreditCard{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.
How to initialize values inside Builders?
To initialize the properties of the object being built, we can create a WithTestValues() method to pass safe defaults or initialize all the fields on the builder directly.
Let’s stick to the safe defaults out-the-box for our example.
publicclassCreditCardBuilder{privatestring_cardNumber="4242424242424242";// ^^^^^privateint_expirationYear=DateTime.Now.Year;// ^^^^^privateint_expirationMonth=DateTime.Now.Month;// ^^^^^privateint_cvv=123;// ^^^// All WithX() methods...publicCreditCardBuild(){returnnewCreditCard{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 that when we use the Builder pattern, the last method in the chain of calls is always the Build() method.
[TestClass]publicclassCreditCardValidationTests{[TestMethod]publicvoidCreditCard_ExpiredYear_ReturnsInvalid(){varvalidator=newCreditCardValidator();// Now, instead of creating cards with the new keyword// or using object mothers, we use a buildervarcreditCard=newCreditCardBuilder()// ^^^^^.WithExpirationYear(DateTime.Now.AddYears(-1).Year).Build();varresult=validator.TestValidate(creditCard);result.ShouldHaveAnyValidationError();}[TestMethod]publicvoidCreditCard_ExpiredMonth_ReturnsInvalid(){varvalidator=newCreditCardValidator();varcreditCard=newCreditCardBuilder()// ^^^^^.WithExpirationMonth(DateTime.Now.AddMonths(-1).Month).Build();varresult=validator.TestValidate(creditCard);result.ShouldHaveAnyValidationError();}}
How to compose 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.
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 ExpiredCreditCard() method to build expired credit cards.
We can simplify our WithCreditCard() method even further to receive a credit card builder, not a credit card object. Like this.
[TestClass]publicclassBookRoomTests{[TestMethod]publicvoidBookRoom_ExpiredCreditCard_ThrowsException(){varservice=newBookingService();// Notice WithCreditCard() receives a builder this timevarrequest=newBookingRequestBuilder().WithGuest("John Doe").WithCreditCard(newCreditCardBuilder().ExpiredCreditCard())// ^^^^^// No extra .Build() here.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.
Want to write readable and maintainable unit tests in C#? Join my course Mastering C# Unit Testing with Real-world Examples on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
From our previous post, we learned about four common 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 result. Writing long names is acceptable since test names should show the purpose behind what they’re testing. When writing tests, prefer good test names over assertion messages.
These are four 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 end of an input string.
We find this naming convention in the book The Art of Unit Testing. 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.”
Grab a free copy of my ebook Unit Testing 101: From Zero to Your First Tests on my Gumroad page. I include these four naming conventions and three more chapters to help you start writing your first unit tests in C#.
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 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”.
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 into separate classes. In the class names, we add the keyword 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 complete 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' showing our sample tests group by class
This last convention uses sentences split 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.
Voilà! That’s how we can name our unit tests. Remember naming things is hard. Pick one of these four naming conventions and stick to it. But, if you inherit a codebase, prefer the convention already in use. 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.
Last time, we covered how to write our first unit tests with C# and MSTest. We started from a Console program and converted it into our first unit tests. We wrote those tests for Stringie, a (fictional) library to manipulate strings with more readable methods. This time, we will cover how NOT to write unit tests. These are four common mistakes we should avoid when writing our first unit tests.
TL;DR
Do not follow a naming convention
Do not use the right assertion methods
Do not have a single assertion per test
Repeat logic in your assertions
Mistake 1: Do not follow a naming convention
First, keep your tests in the right place. Have one test project per project, one test class per class. Add the suffix “Tests” in the name of your test projects and classes.
Choose a naming convention for your test names and stick to it.
In our previous post, we covered two naming conventions. An “ItShould” sentence and the “UnitOfWork_Scenario_ExpectedResult”, a three-part name separated with underscores. You can choose the one you like the most.
That time, for Stringie Remove() method, following the “UnitOfWork_Scenario_ExpectedResult” convention, we wrote test names like these ones:
Every test should tell the scenario under test and the expected result. We shouldn’t worry about long test names. But, let’s stop naming our tests: Test1, Test2, and so on.
Don’t prefix our test names with “Test.” If we’re using a testing framework that doesn’t need keywords in our test names, let’s stop doing that. With MSTest, we have attributes like [TestClass] and [TestMethod] to mark our methods as tests.
Also, don’t use filler words like “Success” or “IsCorrect” in our test names. Instead, let’s tell what “success” and “correct” means for that test. Is it a successful test because it doesn’t throw exceptions? Is it successful because it returns a value? Make your test names easy to understand.
Grab a free copy of my ebook Unit Testing 101: From Zero to Your First Tests on my Gumroad page. I include these four mistakes and three more chapters to help you start writing your first unit tests in C#.
For the Assert part of your tests, make sure to use an assertion library. MSTest, NUnit, and XUnit are the three most popular ones for C#.
Use the right assertion methods of your library. For example, MSTest has assertion methods for strings, collections, and other objects. For a list of the most common MSTest assertions methods, check the MSTest Cheatsheet in Unit Testing 101.
With parameterized tests, we have separate tests. Inside Visual Studio, in the “Test Explorer” menu, we will have one result per each [DataRow] attribute in the parent test.
Visual Studio 'Test Explorer' showing the result outcomes for our parameterized test
It’s easier to troubleshoot parameterized tests when our tests fail for a single test value.
Mistake 4: Repeat logic in your assertions
I can’t stress this enough.
Don’t repeat the logic under test in your assertions. Use known, hard-coded, pre-calculated values instead.
We shouldn’t copy the tested logic and paste it into a private method in our tests to use it in our assertions. We will have code and bugs in two places.
Please, don’t write assertions like the one in this test.
For this test, instead of using the Substring() method to remove the input string, use a known expected value. Write Assert.AreEqual("Hello,", transformed). For example,
[TestMethod]publicvoidRemove_ASubstring_RemovesThatSubstringFromTheEnd(){stringstr="Hello, world!";stringtransformed=str.Remove("world!").From(The.End);// Let's use a known value in our assertionsAssert.AreEqual("Hello,",transformed)}
Voilà! These are four common mistakes when writing our first unit tests. Remember to put your test in the right places following a naming convention. Also, keep one assertion per test, and don’t repeat logic in your assertions. You will have better tests by avoiding these mistakes.
If you want to practice identifying and fixing these mistakes, check my Unit Testing 101 repository. You will find the tests that Stringie developers wrote and some other misatkes mistakes they made. Your mission, Jim, should you choose to accept it, is to fix them.