In my demonstration of the Dependency Injection design pattern, I manually created a mock object. This let me perform unit tests without needing to access a database.
Here is how you can use the Moq library, to eliminate writing the code for manual mock classes.
If you aren’t familiar with dependency injection, please read this article first.
Video version of this demonstration
Manual mock object version
IPlayerDataMapper.cs
namespace Engine.Moq { public interface IPlayerDataMapper { bool PlayerNameExistsInDatabase(string name); void InsertNewPlayerIntoDatabase(string name); } }
This defines the functions needed in the object that is “injected” into the CreateNewPlayer function – by both the “real” object (which acceses the database), and the “mock” object (which doesn’t need a database).
PlayerDataMapper.cs
using System.Data; using System.Data.SqlClient; namespace Engine.Moq { public class PlayerDataMapper : IPlayerDataMapper { private readonly string _connectionString = "Data Source=(local);Initial Catalog=MyGame;Integrated Security=True"; public bool PlayerNameExistsInDatabase(string name) { using(SqlConnection connection = new SqlConnection(_connectionString)) { connection.Open(); using(SqlCommand playersWithThisName = connection.CreateCommand()) { playersWithThisName.CommandType = CommandType.Text; playersWithThisName.CommandText = "SELECT count(*) FROM Player WHERE 'Name' = @Name"; playersWithThisName.Parameters.AddWithValue("@Name", name); // Get the number of player with this name var existingRowCount = (int)playersWithThisName.ExecuteScalar(); // Result is 0, if no player exists, or 1, if a player already exists return existingRowCount > 0; } } } public void InsertNewPlayerIntoDatabase(string name) { using(SqlConnection connection = new SqlConnection(_connectionString)) { connection.Open(); using(SqlCommand playersWithThisName = connection.CreateCommand()) { playersWithThisName.CommandType = CommandType.Text; playersWithThisName.CommandText = "INSERT INTO Player ([Name]) VALUES (@Name)"; playersWithThisName.Parameters.AddWithValue("@Name", name); playersWithThisName.ExecuteNonQuery(); } } } } }
This class implements IPlayerDataMapper, and accesses the database – making it difficult to use in automated tests.
Player.cs
using System; namespace Engine.Moq { public class Player { public string Name { get; private set; } public int ExperiencePoints { get; private set; } public int Gold { get; private set; } public Player(string name, int experiencePoints, int gold) { Name = name; ExperiencePoints = experiencePoints; Gold = gold; } public static Player CreateNewPlayer(string name, IPlayerDataMapper playerDataMapper = null) { // If a PlayerDataMapper was not passed in, use a real one. // This allows us to pass in a "mock" PlayerDataMapper (for testing), // but use a real PlayerDataMapper, when running the program. if(playerDataMapper == null) { playerDataMapper = new PlayerDataMapper(); } if(string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Player name cannot be empty."); } // Throw an exception if there is already a player with this name in the database. if(playerDataMapper.PlayerNameExistsInDatabase(name)) { throw new ArgumentException("Player name already exists."); } // Add the player to the database. playerDataMapper.InsertNewPlayerIntoDatabase(name); return new Player(name, 0, 10); } } }
The CreateNewPlayer function does some validation, using the “injected” (passed in, as a parameter) IPlayerDataMapper object.
MockPlayerDataMapper.cs
using Engine.Moq; namespace TestEngine.Moq.ManualMock { public class MockPlayerDataMapper : IPlayerDataMapper { // This property holds the value for PlayerNameExistsInDatabase to return. // This lets us "mock" the results that we would receive from a real database. public bool ResultToReturn { get; set; } // These functions implement the IPlayerDataMapper interface. public bool PlayerNameExistsInDatabase(string name) { // This is whatever answer we need, for the current unit test. return ResultToReturn; } public void InsertNewPlayerIntoDatabase(string name) { } } }
This is the manually-created mock class, to use in the unit tests.
If you had a large program, you might need to create dozens, or hundreds, of mock classes – to prevent your unit tests from needing to access external resources (e.g., databases, web services, the file system).
TestPlayer.cs
using System; using Engine.Moq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TestEngine.Moq.ManualMock { [TestClass] public class TestPlayer { // Should receive an exception, because the name is an empty string. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_EmptyName() { Player player = Player.CreateNewPlayer(""); } // Should still receive an exception, because the name is an empty string. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_EmptyName_MockedDataMapper() { MockPlayerDataMapper mockPlayerDataMapper = new MockPlayerDataMapper(); Player player = Player.CreateNewPlayer("", mockPlayerDataMapper); } // Should receive an exception, because we set the mock PlayerDataMapper // to say the player name already exists in the database. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_AlreadyExistsInDatabase() { MockPlayerDataMapper mockPlayerDataMapper = new MockPlayerDataMapper(); mockPlayerDataMapper.ResultToReturn = true; Player player = Player.CreateNewPlayer("Test", mockPlayerDataMapper); } // Should succeed, because we set the mock PlayerDataMapper // to say the player name does not already exist in the database. // Also, when it calls the mock InsertNewPlayerIntoDatabase, // the mock mapper will not need a database running. [TestMethod] public void Test_CreateNewPlayer_DoesNotAlreadyExistInDatabase() { MockPlayerDataMapper mockPlayerDataMapper = new MockPlayerDataMapper(); mockPlayerDataMapper.ResultToReturn = false; Player player = Player.CreateNewPlayer("Test", mockPlayerDataMapper); Assert.AreEqual("Test", player.Name); Assert.AreEqual(0, player.ExperiencePoints); Assert.AreEqual(10, player.Gold); } } }
These are the unit tests, using MockPlayerDataMapper objects to eliminate the need to connect to a database when running automated tests.
Simpler mock objects, using Moq
Add Moq to the unit test project, using NuGet
If you aren’t familiar with it, NuGet is a Visual Studio tool to add some third-party libraries to projects.
You could find the library files online (Moq’s are at https://github.com/moq/moq4), download them, and add them as a reference to your project. However, NuGet will do all that for you, with a nice user interface.
Right-click on the TestEngine project (the one we want to add Moq to). Select “Manage NuGet Packages…”
Â
In the NuGet tab, select “Browse” and search for “Moq” – the library we want to add.
There are several add-on libraries that make it easier to use Moq in different types of programs. But, for now, we will only add the one named “Moq”, with over seven million downloads.
Click on the row with “Moq”, and you will see an “Install” button to the right of it.
You will see more information about the library, including any dependencies it has – such as version of the .NET Framework.
Click the “Install” button, to add Moq to the project.
When it is done, you can view the “References” in “TestEngine”, and you should see “Moq”.
Create unit tests that use Moq
Because we already have an existing TestPlayer class, I’ll make a copy of it. We’ll modify that unit test class, replacing the mock objects from the manually-created mock class with objects created by Moq.
This is the new TestPlayer class:
using System; using Engine.Moq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace TestEngine.Moq.UsingMoqLibrary { [TestClass] public class TestPlayer { // Should receive an exception, because the name is an empty string. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_EmptyName() { Player player = Player.CreateNewPlayer(""); } // Should still receive an exception, because the name is an empty string. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_EmptyName_MockedDataMapper() { var mock = new Mock<IPlayerDataMapper>(); Player player = Player.CreateNewPlayer("", mock.Object); } // Should receive an exception, because we set the mock PlayerDataMapper // to say the player name already exists in the database. [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Test_CreateNewPlayer_AlreadyExistsInDatabase() { var mock = new Mock<IPlayerDataMapper>(); mock.Setup(x => x.PlayerNameExistsInDatabase(It.IsAny<string>())).Returns(true); Player player = Player.CreateNewPlayer("Test", mock.Object); } // Should succeed, because we set the mock PlayerDataMapper // to say the player name does not already exist in the database. // Also, when it calls the mock InsertNewPlayerIntoDatabase, // the mock mapper will not need a database running. [TestMethod] public void Test_CreateNewPlayer_DoesNotAlreadyExistInDatabase() { var mock = new Mock<IPlayerDataMapper>(); mock.Setup(x => x.PlayerNameExistsInDatabase(It.IsAny<string>())).Returns(false); Player player = Player.CreateNewPlayer("Test", mock.Object); Assert.AreEqual("Test", player.Name); Assert.AreEqual(0, player.ExperiencePoints); Assert.AreEqual(10, player.Gold); } } }
To use the Moq library, we need to add “using Moq;” (see line 4).
In the unit tests, instead of creating an object of the old MockPlayerDataMapper, and passing it into the Player.CreateNewPlayer function, we will use Moq to create the mock object.
Line 24 is how Moq creates an object that implements the IPlayerDataMapper interface.
For this first test, we don’t need to configure that object to do any specific behavior, when it is called. So, we only need to change line 26, to pass in the “Object” property of the “mock” object. This is the object that will be used inside the CreateNewPlayer function, during this unit test.
The power of Moq is that you can configure your mock object to return any results you want.
In the Test_CreateNewPlayer_AlreadyExistsInDatabase unit test, when the CreateNewPlayer function calls PlayerNameExistsInDatabase, we want the mock object to return “true”.
For this unit test, I don’t care what value is passed into the PlayerNameExistsInDatabase function. I know it will be “Test” (from line 38), but that is not important for this test. I also know that I want that function to return “true”, regardless of the value passed into it (for this test).
This is what line 36 does.
It configures, by using the “Setup” function on the mock object, the PlayerNameExistsInDatabase to accept any string parameter (the “It.IsAny<string>()” and have it “Returns(true)”.
If I was building a more complex test, I could create multiple “Setup” lines, and have the mock object return different results, based on different parameters I might pass in. But, for this simple test, we only need the one setup line.
On line 49, we have the same setup, but tell Moq to have the function return “false” – because that is the scenario we want to test in this unit test.
To see more about what you can do with Moq (including more complex test scenarios), you can view the Moq Quickstart page, or read Roy Osherove’s “The Art of Unit Testing: with examples in C#”.