Now that we've discussed why, when, and how we want to write unit tests, it's time to set up our project to allow us to do so. In this post, we will create "fluent" mocking classes that will allow us to easily write unit tests using Moq, XUnit, and ASP.NET 5.0. In subsequent posts, we will use these fluent mocked classes to write actual tests.
App Architecture and Data Model
Here is the basic architecture of our app:
At the lowest layer, we have the Repositories. These classes interface directly with the data store, whatever that may be.
In the next layer are the Services. These classes can inject multiple Repositories and combine data from them to serve the Controllers. Their primary reason to exist is to enforce the idea of "fat models, thin controllers" that the MVC pattern wants, where the "models" are really the entire business layer (Repositories, Services, model classes, etc.).
In the highest layer are the Controllers. These are MVC Controller classes that call the Services to get their data, as well as handling Views and other things that controllers normally do.
For reference, the data model of this project is as follows:
- Leagues are sports leagues, comprising many Teams.
- Teams are teams of players in the same sport, comprising many Players.
- Players are individuals who play for a specified Team in that Team's League.
This post, like all in this series, uses a sample project hosted on GitHub; you may want to use that project to follow along with the examples herein.
Mocking Basics
One of the goals a test suite should have for testing an application like this one is to test each layer independently of the others. That means that in order to test the Services layer, for example, we need to run tests that are independent of the functionality in the Repositories layer below, on which the Services depend.
To do this, we "mock" the Repositories, which means we set up a "fake" class which returns a known value for certain calls. We do this for each call the Service class in question will make to a Repository, or, more generally, to each dependency the class being tested has.
The most popular library for doing this kind of mocking in the ASP.NET world is Moq. Hence, we will use that library during this project.
Using Moq
Moq is a NuGet package, so before we can use it, we need to add it to our project via NuGet.
The first way we use Moq is to set up a "fake" or "mocked" instance of a class, like so:
var mockTeamRepository = new Mock<ITeamRepository>();
The created mockTeamRepository
object can then be injected into classes which need it, like so:
However, as of yet our mocked class doesn't actually do anything; we need to set up any methods that need to be called or return values. For that, we can do the following:
mockTeamRepository.Setup(x => x.GetByID(It.IsAny<int>()))
.Returns(new Team());
Let's break down what this is actually doing:
- The call to
.Setup()
is specifying that when the given method is invoked (in this case,GetByID()
) the mock should return whatever is specified by the call to.Returns()
(in this case, anew Team()
). - The code
It.IsAny<int>()
specifies that the method will be called and return the specified item for any validint
. More examples are on the Moq GitHub page.
So, for any given integer, the mocked class will return a new Team()
.
Occasionally, you might want to have a method throw an exception rather than return an object. You can do this using the Throws()
method:
mockTeamRepository.Setup(x => x.GetByID(It.IsAny<int>()))
.Throws(new Exception());
Building a Fluent Mock
All of this is fine and dandy, because it allows us to easily mock our layers so that we can test them independently. But in real-world unit test scenarios, we often have to use those mocks in more than one test, sometimes quite a few more. One of the ways my team and I have discovered to limit the amount of repeated code in our tests is to build a "fluent" mock class.
Let's start with a class that inherits from Mock<>
:
public class MockTeamRepository : Mock<ITeamRepository> { }
For each method we want to mock, we need to call .Setup()
, but we want to do so in a way that allows us to "chain" method calls when creating instances of these mock classes. This allows for a "fluent" set of calls.
public class MockTeamRepository : Mock<ITeamRepository>
{
public MockTeamRepository MockGetByID(Team result)
{
Setup(x => x.GetByID(It.IsAny<int>()))
.Returns(result);
return this;
}
public MockTeamRepository MockGetForLeague(List<Team> results)
{
Setup(x => x.GetForLeague(It.IsAny<int>()))
.Returns(results);
return this;
}
}
We can then use this new "fluent" interface to create a mock instance:
var mockTeamRepo = new MockTeamRepository()
.MockGetByID(new Team())
.MockGetForLeague(new List<Team>());
Verifying Calls
Moq also includes a "Verify" feature. This allows us to ensure that a particular mocked method was called a specified number of times.
You might want to use this feature, for example, when you need to do some kind of verification before you make a call to a mocked class. Let's look at the Search()
method of TeamRepository
for an example.
public List<Team> Search(TeamSearch search)
{
//If we are searching for an invalid or unknown League...
var isValidLeague = _leagueRepo.IsValid(search.LeagueID);
if (!isValidLeague)
return new List<Team>(); //Return an empty list.
//Otherwise get all teams in the specified league...
var allTeams = _teamRepo.GetForLeague(search.LeagueID);
//... and filter them by the specified Founding Date and Direction.
if(search.Direction == Enums.SearchDateDirection.OlderThan)
return allTeams.Where(x => x.FoundingDate <= search.FoundingDate).ToList();
else return allTeams.Where(x => x.FoundingDate >= search.FoundingDate).ToList();
}
In this method, it is possible for the repository-layer function GetForLeague()
to not be called at all; this happens when the league ID is invalid. A Verify method would allow us to ensure during our test that, when the League ID is invalid, the function GetForLeague()
is never called.
We could then add a new function to the MockTeamRepository
class, like so:
public class MockTeamRepository : Mock<ITeamRepository>
{
...
public MockTeamRepository VerifyGetForLeague(Times times)
{
Verify(x => x.GetForLeague(It.IsAny<int>()), times);
return this;
}
}
The Times
class is defined by Moq for use in these scenarios; it allows us to specify the number of times we expect the method to be called. For example, if we expect it to be called exactly once, we could use this method like so:
//Arrange
var mockTeamRepo = new MockTeamRepository()
.MockGetByID(new Team())
.MockGetForLeague(new List<Team>());
var teamSearch = new TeamSearch()
{
LeagueID = 1
}
var teamService = new TeamService(mockTeamRepo.Object);
//Act
var result = teamService.Search(teamSearch);
//Assert
mockTeamRepo.VerifyGetForLeague(Times.Once());//Expect this to be called exactly once.
There are many values for the Times
class we could use:
mockTeamRepo.VerifyGetForLeague(Times.Once());
mockTeamRepo.VerifyGetForLeague(Times.AtLeastOnce());
mockTeamRepo.VerifyGetForLeague(Times.AtMostOnce());
mockTeamRepo.VerifyGetForLeague(Times.Never());
mockTeamRepo.VerifyGetForLeague(Times.AtMost(3)); //At most three calls
mockTeamRepo.VerifyGetForLeague(Times.Exactly(5)); //Exactly five calls
Building the Mock Classes
Now that we've seen how we can build a fluent mocked class, let's practice building them.
Let's start with TeamRepository
. Here's the code for it:
public class TeamRepository : ITeamRepository
{
public Team GetByID(int id)
{
throw new NotImplementedException();
}
public List<Team> GetForLeague(int leagueID)
{
throw new NotImplementedException();
}
}
Waaaait a minute, why is nothing implemented?! To prove a point. The actual implementation details do not matter when mocking a class. So, let's write up some rules for this class's methods.
GetByID()
should return aTeam
object if one is found, and throw an exception if one is not.GetForLeague()
should return a populated list if the league ID is valid and found, and an empty list if it is not.
With these rules, we can create the mocked class. Here it is:
public class MockTeamRepository : Mock<ITeamRepository>
{
public MockTeamRepository MockGetByID(Team result)
{
Setup(x => x.GetByID(It.IsAny<int>()))
.Returns(result);
return this;
}
public MockTeamRepository MockGetByIDInvalid()
{
Setup(x => x.GetByID(It.IsAny<int>()))
.Throws(new Exception());
return this;
}
public MockTeamRepository VerifyGetByID(Times times)
{
Verify(x => x.GetByID(It.IsAny<int>()), times);
return this;
}
public MockTeamRepository MockGetForLeague(List<Team> results)
{
Setup(x => x.GetForLeague(It.IsAny<int>()))
.Returns(results);
return this;
}
public MockTeamRepository VerifyGetForLeague(Times times)
{
Verify(x => x.GetForLeague(It.IsAny<int>()), times);
return this;
}
}
This fluent mock class and others like it can be used in our tests, like so (example pulled directly from the GitHub project):
[Fact]
public void TeamService_Search_OlderThan_Valid()
{
//Arrange
var mockTeams = GetMockTeams();
var mockTeamRepo = new MockTeamRepository().MockGetForLeague(mockTeams);
var mockLeagueRepo = new MockLeagueRepository().MockIsValid(true);
var teamService = new TeamService(mockTeamRepo.Object, mockLeagueRepo.Object);
var searchParams = new TeamSearch()
{
LeagueID = 1,
FoundingDate = new DateTime(2013, 1, 1),
Direction = SearchDateDirection.OlderThan
};
//Act
var results = teamService.Search(searchParams);
//Assert
Assert.NotEmpty(results);
mockLeagueRepo.VerifyIsValid(Times.Once());
mockTeamRepo.VerifyGetForLeague(Times.Once());
}
Check out the Mocks folder in the sample project for more examples.
Summary
Using Moq, we can create fluent mocked classes that allow us to write our unit tests more easily and concisely. These mock classes can be reused by our many test classes, and allow for both setting up mock methods and return value and verifying that said methods were actually invoked.
My hope is that this architecture will prove useful to you, my readers. If you see a way to improve on it, see a mistake I made, or just want to tell us all about how your unit testing is going, use the comments below!
Don't forget to check out the sample project on GitHub!
In the next part of this series, we're going to use the Moq fluent mocked classes created here, as well as XUnit, to actually write some unit tests for the business layer of our sample application.
Happy Testing!