Test Driven Development — Fake it to make it
A light introduction, and demonstration of mocks from the perspective of a student
Mocks are a crucial aspect of unit testing; allowing developers to focus on one aspect of code within the solution at a time. Mocking objects isolates each component of code without worrying about dependency on other components. This isolation allows for the testing of individual methods, even if the dependent classes are yet to be implemented. We use mocks to simulate the behaviour of complex, real objects which is useful in circumstances where instantiating a real instance of the object is impractical or outside of the scope of our test. Mocks inherit the same interface as the object they simulate, because of this they have the same signatures, so from a black box perspective there’s no way of differentiating between a mocked object, and the real thing.
Wikipedia states that mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.
Common pitfalls that are avoided with mocks
When writing code we often depend on calls to third party services, database polls, and system calls. All of which result in long wait times, incur costs, and reduce your productivity. Instead of relying on these calls during the testing phase of development we can instead mock these objects resulting in faster tests.
1. Calls to third party services
Imagine we’re writing an application that uses SendGrid to send an email to our users after they’ve signed up, the email asks them to verify by clicking the provided link before we can let them use our application. We’ve just written some code that generates an authentication link, composes the email, and sends it to the users supplied email address. We’d like to test out this service but we don’t want to call SendGrid each time, as we’ll start incurring extra cost. We can instead use mocks.
2. Database Polling
We’ve now written some code that handles the process of logging an existing user into our application. The code involves retrieving the hashed password for the user from our database, and then comparing it to the hash the user has just supplied. If it’s correct they’re authenticated. When we go to test this instead of calling a function to poll the database we instead mock that function and return the hashed password without any delay allowing us to focus on code instead of interaction with a database.
3. System calls
On a different note we’re writing a script that will send diagnostics to the printer when certain conditions are met. Having to check the printer each time you run tests is going to get pretty tedious, so we instead mock this.
Demonstration using C# & Moq
In this post we’ll be using Moq, it’s simple to use, and is employed in both simple, and enterprise solutions. Follow along, or download the completed solution here. We’ll model this demonstration off of the database polling problem presented earlier
Getting Started
Create a Console Application inside Visual Studio, and then add a Unit Test Project to the solution.
Install Moq
To install Moq, you can follow the instructions from their GitHub, or just use Nuget:
Install-Package -projectname MoqUnitTestsMedium.Tests Moq
Database Retriever
We need an interface that handles the interaction with the database. Since we’ll be focusing on unit tests regarding the authentication process we don’t care much about this:
public interface IDatabaseRetriever{ string RetrievePassword(string username);}public class DatabaseRetriever : IDatabaseRetriever{ public string RetrievePassword(string username) { return "Not Implemented"; }}
The DatabaseRetriever class isn’t used by our tests but its been included to help understand the problem mocking solves.
Login
We now have a class that will handle authentication with a single function Authenticate which takes the username, as well as the supplied hash and returns true or false depending on whether the supplied hash is the same as the one we have stored in our database.
public bool Authenticate(string username, string suppliedHash){ var storedHash = _databaseRetriver.RetrievePassword(username); if (storedHash == suppliedHash) return true; else return false;}
Our class has one dependency IDatabaseRetriever
and it’s this that we wish to mock.
private readonly IDatabaseRetriever _databaseRetriver;public Login(IDatabaseRetriever databaseRetriver){ _databaseRetriver = databaseRetriver;}
LoginTests
Since our Authenticate method can either return true or false, we’ll write two test methods: Authenticate_True
and Authenticate_False
We’ll be following the AAA pattern for all of our unit tests.
Authenticate_True
//Arrange
var mock = new Mock<IDatabaseRetriever>();
mock.Setup(x => x.RetrievePassword("user1")).Returns("password");
What we’ve done is used Moq to create a mock instance of IDatabaseRetriever
. In the second line we told Moq that anytime RetrievePassword
is called with the parameter user1
it should return password
. We’re now able to test out the functionality of Authenticate
without worrying about querying to a database.
//Act
var login = new Login(mock.Object);
var isAuthenticate = login.Authenticate("user1", "password");
In the first line we’ve instantiated an instance of Login
and passed our mocked version of IDatabaseRetriever
into the constructor.
//Assert
Assert.AreEqual(true, isAuthenticate);
We finish off the test by checking that Authenticate
returned true.
Authenticate_False
//Arrangevar mock = new Mock<IDatabaseRetriever>();mock.Setup(x => x.RetrievePassword("user1")).Returns("password");//Actvar login = new Login(mock.Object);var isAuthenticate = login.Authenticate("user1", "notmypassword");//AssertAssert.AreEqual(false, isAuthenticate);
The Arrange portion remains the same but we instead pass notmypassword
into authenticate
to make sure the function returns false when suppliedHash
is different to storedHash
.
Takeaway
After a simple but rather contrived example, I hope you’re able to see the benefits of working with mocks, as it allows you to simplify your testing which in turn simplifies your workflow, and increases productivity.