Telerik blogs
How ToT2 Dark_1200x303

The quickest way to take your unit testing to the next level is with a mocking framework. Let’s see how they work together.

What Is Mocking?

It’s time to take your unit testing to the next level. You’ve implemented either NUnit or xUnit or MSTest in your projects. You’ve gotten your code coverage to 80+%. But there are just some things that are hard to test or validate in your project.

How do you test the “business logic” in your repository class? How do you test your dependent web service or database? Yeah, you can write special unit tests and create fake objects to mimic these dependencies, but why waste your time writing code that does not ship with the end product. Or write a lot of code with the ExcludeFromCoverage attribute. 😊 Well this is where mocking comes in.

Mocking is a process that allows you to create a mock object that can be used to simulate the behavior of a real object. You can use the mock object to verify that the real object was called with the expected parameters, and to verify that the real object was not called with unexpected parameters. You can also verify that the real object was called the expected number of times. And you can verify that the real object was called with the expected parameters and that the real object was not called with unexpected parameters. The possibilities are endless.

Mocking comes in three flavors: fakes, stubs and mocks. The fakes are the simplest. They are used when you want to test the behavior of a class that has no dependencies. The stubs are used when you want to test the behavior of a class that has dependencies and you do not want to change the behavior. The mocks are used when you want to test the behavior of a class that has dependencies and potentially modify behaviors.

For more information on mocking and the differences between stubs, fakes and mocks read the Fakes, Stubs and Mocks blog post.

Getting Started With Mocking

First, you’ll need a mocking framework to get started as you don’t want to manage the life cycle of all mock objects. Something like Telerik JustMock or their free version JustMock Lite.

JustMock: The fastest, most flexible mocking tool for crafting unit tests

A mocking framework is what you use to create the objects that “pretends” to be the object(s) you are testing.

Arrange, Act, Assert

Now that you have a mocking framework, let’s get started with the primary parts of the unit testing process—Arrange, Act, Assert. Arrange, Act, Assert (or AAA) is a common term used to describe the process of setting up the test environment, executing the test, and verifying the results. It’s a best practice in unit testing. Basically, each of your unit tests should have these three parts:

  • Arrange: Set up the test.
  • Act: Execute the test.
  • Assert: Verify the results.

When I write tests, in this case using xUnit, I generally start with this “stub” pattern:

[Fact]
public void GetContact_WithAnInvalidId_ShouldReturnNull()
{
    // Arrange 
 
    // Act
 
    // Assert
}

The method name follows a consistent format:

  • [Fact]: This is a fact test.
  • public void: This is a public method.
  • GetContact: This is the method you are testing.
  • _WithAnInvalidId_: Whatever variables you are using, in this example, an invalid id.
  • ShouldReturnNull: The expected outcome.

While this convention is not required, I tend to use it so when I am looking at the results, or another engineer is looking at the code, they can see the intent of the test.

All Unit Tests Pass Successfully

Mocking Stuff

There are a lot of different types of things to mock, like services, databases, queues and other types of dependencies. For this introductory example, I am going to demonstrate different ways to test a class that requires a dependency of a database. I’ll be using xUnit and Telerik JustMock for these examples.

The project used in this example can be found here. This project is a C# project that was built on my Twitch stream. The application provides a way to manage a list of contacts. It uses a variety of technologies including:

  • Web API
  • SQL Server
  • ASP. NET Core
  • ASP. NET Core MVC
  • Azure Storage
  • Azure Functions

With all these dependencies, I needed a way to validate that these dependencies are working as expected. And before you ask, no, I am not testing the functionality of SQL Server or Azure Storage or Azure Functions. I am only testing the interaction with this service. That’s where mocking comes in. For the rest of this post, I’ll focus on testing the ContactManager class and mocking the ContactRepository.

Before we get started, let’s take a look at the ContactManager class. The ContactManager implements the IContactManager interface. This is what we are testing.

public interface IContactManager
{
    Contact GetContact(int contactId);
    List<Contact> GetContacts();
    List<Contact> GetContacts(string firstName, string lastName);
    Contact SaveContact(Contact contact);
    bool DeleteContact(int contactId);
    bool DeleteContact(Contact contact);
    List<Phone> GetContactPhones(int contactId);
    Phone GetContactPhone(int contactId, int phoneId);
    List<Address> GetContactAddresses(int contactId);
    Address GetContactAddress(int contactId, int addressId);
    /// Other methods removed for brevity
}

Full code for this class can be found here.

We’ll be mocking the ContactRepository object which implements the IContactRepository interface.

public interface IContactRepository
{
    Contact GetContact(int contactId);
    List<Contact> GetContacts();
    List<Contact> GetContacts(string firstName, string lastName);
    Contact SaveContact(Contact contact);
    bool DeleteContact(int contactId);
    bool DeleteContact(Contact contact);
    List<Phone> GetContactPhones(int contactId);
    Phone GetContactPhone(int contactId, int phoneId);
    List<Address> GetContactAddresses(int contactId);
    Address GetContactAddress(int contactId, int addressId);
    /// Other methods removed for brevity
}

While objects that are being mocked don’t need to be interfaces, it certainly helps. The IContactManager interface is a contract that defines the methods that interact with a contact. The ContactManager class implements the IContactManager interface, in this case. However, one thing to note is that the ContactManager requires an IContactRepository dependency, which is what we are going to mock.

The IContactRepository interface defines the contract with the database, which we do not want to test. This is where mocking comes in. We want to be able to test that the logic in the ContactManager class is working as expected without going back and forth with the database. This allows us to test things like validation or objects on save, returning the correct exceptions when things go wrong, etc.

Our First Mock

Let’s start with the most common test. Let’s validate that a call to GetContacts returns a list of contacts. We’ll start with the simplest test, and then move to more complex tests.

The signature of GetContacts is:

Task<List<Contact>> GetContacts();

If we start with our template from above, we should stub out a test that looks like this:

public void GetContacts_ShouldReturnLists()
{
    // Arrange
 
    // Act
 
    // Assert
}

Arrange: The Test

Now, let’s look at the arrange part. For the arrange part, we need to setup the mocks so that the mocking framework knows what to mimic or mock. Here’s the arrange part for the GetContacts_ShouldReturnLists method:

var mockContactRepository = Mock.Create<IContactRepository>();
Mock.Arrange(() => mockContactRepository.GetContacts())
  .Returns(new List<Contact>
  {
      new Contact { ContactId = 1 }, new Contact { ContactId = 2 }
  });
var contactManager = new ContactManager(mockContactRepository);

On line 1, we create a variable, mockContactRepository, that is the mock of the IContactRepository interface. Line 2, we create a mock of the GetContacts method. Lines 3-6, we create a list of contacts and tell the mock framework to return this object when a call is made to GetObjects. Finally, on line 7, we create a new ContactManager object and pass in the mock IContactRepository object.

Act: Execute on the Test

In this case, the act is trivial. We just call the GetContacts method on the ContactManager object.

var contacts = contactManager.GetContacts();

This should return a list of contacts with two contacts with the ids of 1 and 2.

Assert: Verify the Results

Let’s validate that the list of contacts has two contacts.

Assert.NotNull(contacts);
Assert.Equal(2, contacts.Count);

Line 1 is checking that the list of contacts is not null. Line 2 is checking that the list of contacts has two contacts.

The Complete Test

[Fact]
public void GetContacts_ShouldReturnLists()
{
    // Arrange
    var mockContactRepository = Mock.Create<IContactRepository>();
    Mock.Arrange(() => mockContactRepository.GetContacts())
      .Returns(new List<Contact>
      {
        new Contact { ContactId = 1 }, new Contact { ContactId = 2 }
      });
 
    var contactManager = new ContactManager(mockContactRepository);
 
    // Act
    var contacts = contactManager.GetContacts();
 
    // Assert
    Assert.NotNull(contacts);
    Assert.Equal(2, contacts.Count);
}

Mocking With Ranges

There is a method in the ContactManager called GetContact that requires an integer as a parameter. In our business case, the identifier of a contact is a positive number (integer). So let’s set up some test that makes sure a call to get GetContact with a negative number returns null and a call to get GetContact with a positive number returns a contact.

For this, we’ll use a feature called matchers. Matchers let you ignore passing actual values as arguments used in mocks. Instead, they give you the possibility to pass just an expression that satisfies the argument type or the expected value range. This means that we don’t have to write a test for each possible value. We can just write a test for the range of values. We are going to use the InRange matcher for our two tests.

For the test, GetContact_WithAnInvalidId_ShouldReturnNull where we expect a null return, we would arrange the test like this:

Mock.Arrange(() => 
  mockContactRepository.GetContact(Arg.IsInRange(int.MinValue, 0, RangeKind.Inclusive)))
    .Returns<Contact>(null);

In this arrangement, we are saying that when a call to GetContact is made with an argument that is in the range of int.MinValue to 0, inclusive, we should return null.

Our act and assert look like:

// Act
var contact = contactManager.GetContact(-1); // Any number less than zero
 
// Assert
Assert.Null(contact);

For the test, GetContact_WithAnValidId_ShouldReturnContact, we would arrange the test like this:

Mock.Arrange(() =>
  mockContactRepository.GetContact(Arg.IsInRange(1, int.MaxValue, RangeKind.Inclusive)))
    .Returns(
    (int contactId) => new Contact
    {
        ContactId = contactId
    });
 
var contactManager = new ContactManager(mockContactRepository);
const int requestedContactId = 1;

This one required a little bit more work because we needed to specify an object to return, lines 3 to 6, and a value for the contact id, line 9, to validate in our test.

Our act and assert look like:

// Act
// Assumes that a contact record exists with the ContactId of 1
var contact = contactManager.GetContact(requestedContactId);
 
// Assert
Assert.NotNull(contact);
Assert.Equal(requestedContactId, contact.ContactId);

Mocking With Exceptions

The GetContacts method has an overload which expects two string parameters, one for first name and the other for last name. The method also requires that the first name and last name are not null or empty. If so, it should throw an ArgumentNullException. Let’s create a test that validates that a call to GetContacts with an empty first name and last name throws the exception.

Let’s arrange the test like this:

// Arrange 
var mockContactRepository = Mock.Create<IContactRepository>();
Mock.Arrange(() =>
    mockContactRepository.GetContacts(null, Arg.IsAny<string>()));
 
var contactManager = new ContactManager(mockContactRepository);

Here we are passing a null for the FirstName parameter and using the Arg.IsAny<string> matcher for the LastName parameter which will match any string.

Our act, which is also an assert, looks like this:

// Act
ArgumentNullException ex =
  Assert.Throws<ArgumentNullException>(() => contactManager.GetContacts(null, "Guadagno"));

Here we are creating a variable ex which is of type ArgumentNullException and then we are asserting that the GetContacts method throws an ArgumentNullException when called with the FirstName parameter set to null and the LastName parameter set to Guadagno.

Then in the assert, we are checking that the exception message is correct.

// Assert
Assert.Equal("firstName", ex.ParamName);
Assert.Equal("FirstName is a required field (Parameter 'firstName')", ex.Message);

Note, JustMock supports an alternative way of testing that an exception is thrown. We can use Mock.Arrange when setting up the test to throw the exception. We can use the Throws<T> matcher to assert that an exception of a specific type is thrown.

Mock.Arrange(() => contactManager.GetContacts(null, "Guadagno"))
  .Throws<ArgumentNullException>("FirstName is a required field (Parameter 'firstName')"); 

Complete Contact Tests

The complete code for the ContactManager class can be found here.

The complete code for the ContactManagerTest class can be found here.

Wrapping Up

This just scratches the surface of mocking. There are many more ways to mock using a mocking framework like JustMock. Maybe we’ll cover more in a future post.

JustMock is available for free trial so you can see it in action. It’s also included as part of the robust Telerik DevCraft bundle.


Joseph-Guadagno
About the Author

Joseph Guadagno

Joe Guadagno is a Director of Engineering at Rocket Mortgage, the US’s largest mortgage lender, based in Detroit, Michigan. He has been writing software for over 20 years, has been an active member of the .NET community, serving as a Microsoft MVP in .NET for more than 10 years. At Rocket Mortgage, he leads three software development teams building and modernizing internal services. He has spoken throughout the United States and at international events on topics ranging from Microsoft .NET, Microsoft Azure, Ionic, Bootstrap and many others (see the complete list). When not sitting at a computer, Joe loves to hang out with his family and play games, as well as checking out the latest in Home Automation. You can connect with Joe on Twitter at @jguadagno, Facebook at JosephGuadagnoNet, and on his blog at https://www.josephguadagno.net.

Related Posts

Comments

Comments are disabled in preview mode.