What is a mocking framework? Why is it useful?
Today I want to talk about mocking frameworks and why they are useful. In order to do that I first need to propose a scenario to you in which we will be testing a piece of functionality in an application. I will show you how you would test it without a mocking framework, and then I will show you how to test it with one.
Let's pretend we have a driver class like this:
public class Driver
{
private IVehicle vehicleToDrive;
public Driver(IVehicle vehicleToDrive)
{
this.vehicleToDrive = vehicleToDrive;
}
public bool EvasiveManeuvers(bool alertOffendingDriver)
{
bool success = false;
if (alertOffendingDriver)
success = this.vehicleToDrive.ApplyBrakes() && this.vehicleToDrive.HonkHorn();
else
success = this.vehicleToDrive.ApplyBrakes();
return success;
}
}
Notice that our Driver
class has a dependency on an interface called IVehicle
. Let's define that interface.
public interface IVehicle
{
///<summary>
///Honks the vehicle's horn.
///</summary>
///<returns>
///True if the action was successful.
///</returns>
bool HonkHorn();
///<summary>
///Applies the vehicle's brakes.
///</summary>
///<returns>
///True if the action was successful.
///</returns>
bool ApplyBrakes();
}
Now that we have our Driver
class we need to write a couple unit tests for it. Before we can do that however, we have to figure out what we are going to pass into the constructor to satisfy the dependency on IVehicle
. Many people would think at this point that they need to finish whatever component implements the IVehicle
interface before they can test the Driver
class. That is not the case. In fact, if you passed a completed component for IVehicle
you might as well not even write unit tests. The whole point of unit testing is to test things in isolation. Testing the Driver
class with a full-fledged version of IVehicle
is not unit testing; that is getting closer to integration testing which is a whole different ball game. If a test on your Driver
class fails, you couldn't be sure it was the Driver
class's fault or if something went wrong within the class that was passed in as a dependency.
The fact that the dependency is an interface makes this an easy fix. We will just create a fake implementation of IVehicle
.
public class FakeVehicle : IVehicle
{
public int CalledHonkHorn = 0;
public int CalledApplyBrakes = 0;
public bool HonkHorn()
{
this.CalledHonkHorn++;
return true;
}
public bool ApplyBrakes()
{
this.CalledApplyBrakes++;
return true;
}
}
Notice the global integers we defined? In our unit tests we would like a way to verify that the HonkHorn()
and ApplyBrakes()
methods actually got called and how many times they got called. Now that we have something we can pass into our Driver
class when we instantiate it, we can write our tests. We want to test two behaviors: Can a driver evade trouble? And can a driver evade trouble while alerting the offending driver to his mistake?
[TestMethod]
public void Can_Evade_Trouble()
{
// Arrange (set up a scenario)
FakeVehicle fakeVehicle = new FakeVehicle();
Driver target = new Driver(fakeVehicle);
// Act (attempt the operation)
bool success = target.EvasiveManeuvers(false);
// Assert (verify the result)
Assert.IsTrue(success);
Assert.IsTrue(fakeVehicle.CalledHonkHorn == 0);
Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
}
[TestMethod]
public void Can_Evade_Trouble_And_Alert_Offending_Driver()
{
// Arrange (set up a scenario)
FakeVehicle fakeVehicle = new FakeVehicle();
Driver target = new Driver(fakeVehicle);
// Act (attempt the operation)
bool success = target.EvasiveManeuvers(true);
// Assert (verify the result)
Assert.IsTrue(success);
Assert.IsTrue(fakeVehicle.CalledHonkHorn == 1);
Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
}
This is all great. We have a successful unit test that verifies both that the EvasiveManeuvers()
method returns true
and that the ApplyBrakes()
method on the implementing class of IVehicle
was called exactly one time in both tests, as well as the HonkHorn()
method was called exactly once in the second test and never in the first test. Take note that in real Test Driven Development (TDD) we would have written the tests first, and then wrote the code we were testing. However, for our purposes it was easier to show you what we had and how we might test it.
There is one thing that is rather annoying about writing tests in this manner. We have to write a special class (FakeVehicle
) in order to even instantiate Driver
. Writing a special class for every dependency can get VERY tiring. This is where a mocking framework comes in. The framework I am going to use is called Moq; it's syntax is unique but after using it for a bit I think you'll agree that it is very fluent and easy to write. Make sure you have the Moq.dll assembly referenced in your project and that your unit test class has the using Moq;
declaration at the top. Now let's rewrite our unit tests to use Moq and then I will explain it in greater detail afterward.
[TestMethod]
public void Can_Evade_Trouble()
{
// Arrange (set up a scenario)
Mock<IVehicle> mock = new Mock<IVehicle>();
mock.Setup(x => x.ApplyBrakes()).Returns(true);
Driver target = new Driver(mock.Object);
// Act (attempt the operation)
bool success = target.EvasiveManeuvers(false);
// Assert (verify the result)
Assert.IsTrue(success);
mock.Verify(x => x.HonkHorn(), Times.Never());
mock.Verify(x => x.ApplyBrakes(), Times.Once());
}
[TestMethod]
public void Can_Evade_Trouble_And_Alert_Offending_Driver()
{
// Arrange (set up a scenario)
Mock<IVehicle> mock = new Mock<IVehicle>();
mock.Setup(x => x.HonkHorn()).Returns(true);
mock.Setup(x => x.ApplyBrakes()).Returns(true);
Driver target = new Driver(mock.Object);
// Act (attempt the operation)
bool success = target.EvasiveManeuvers(true);
// Assert (verify the result)
Assert.IsTrue(success);
mock.Verify(x => x.HonkHorn(), Times.Once());
mock.Verify(x => x.ApplyBrakes(), Times.Once());
}
Believe it or not, with only a few lines of code we got rid of the need for the FakeVehicle
class to exist. Moq took our interface and dynamically built a class that implements it behind the scenes. By default all the members of the class it built would return default values. Since the default value of a Boolean is false, the HonkHorn()
and ApplyBrakes()
methods on the mocked class would return false. Obviously we want them to return true when called. To accomplish this we simply make use of Moq's Setup()
method. This method accepts a lambda expression which allows you to navigate to the method on the interface that you want to define in a strongly-typed manner. If this is confusing, picture the alternative; if Moq did not accept a lambda expression then you would have to give it a method name in a magic string, like this:
mock.Setup("HonkHorn").Returns(true);
That's kind of ugly and the compiler would not throw an exception if the method on the interface changed. You wouldn't know your test had an issue until you ran it. Moq uses lambdas to ensure that everything is strongly typed. In addition to that, if the method accepted any arguments then it's a simple matter of passing them in within the lambda, like this:
mock.Setup(x => x.HonkHorn("loudly").Returns(true));
If your HonkHorn()
method accepted a string argument then this setup would instruct the dependency class to return true when the HonkHorn()
method was called and passed the string "loudly"
. If you don't have a need to test a specific argument being passed in, Moq provides a clever little class called It
. Yes that's right, it's just called It
, but it contains some very helpful methods. For example, if I wanted to return true when the HonkHorn()
method got passed a string with any value, I would just do this:
mock.Setup(x => x.HonkHorn(It.IsAny<string>())).Returns(true);
It's that straight-forward. Moq gives you complete control over how the mocked object is created, what it does, and how it was used by the test. To use Moq all you have to do is create an instance of Mock<T>
, T being the type that you want to mock. After you have your instance you can call the Setup()
method as many times as you wish to setup methods and properties on the mocked object to return what you need them to return for testing purposes. If you need a property to return a collection of something then all you have to do is define a collection of something in your test class and then pass that collection into the .Returns()
method following your Setup()
. Now whenever that property is accessed on the mocked dependency it will return the collection you defined. Just keep in mind that the mocked object is NOT what you are testing; you mock dependencies so you can pass them into whatever class/component you ARE testing.
Once you have all that setup you can instantiate the component you are testing and pass in the mocked object by calling mock.Object
. mock.Object
is a really generic sounding property to store the mocked object we created, but that's where it is stored. Do not try to pass the instance of Mock<T>
itself into your component. It can be confusing at first but remember that your Driver
needs an instance of IVehicle
; Mock<T>
is not of type IVehicle
, it is of type Mock<T>
. To get the created instance of IVehicle
you must access the Object
property on your instance of Mock<T>
. In other words:
//Do this
Mock<IVehicle> mock = new Mock<IVehicle>();
Driver driver = new Driver(mock.Object);
//Not this
Mock<IVehicle> mock = new Mock<IVehicle>();
Driver driver = new Driver(mock);
After that you can go ahead and perform whatever functions you intend to test. In our case we called driver.EvasiveManeuvers(alertOffendingDriver)
and tested that the returned result was true. The next thing we tested was how many times the HonkHorn()
and ApplyBrakes()
methods were called on the dependency class. To do this with the fake class we created we had to actually add fields to track how many times the methods were called. With Moq we don't have to do anything. It automatically tracks how many times we called the method. All we have to do is call mock.Verify()
passing in a lambda with the method we want to check and the number of times we expected it to be called. If the verify fails it will throw an exception, causing our test to fail.
In its most basic form that is the usage of Moq. Hopefully now you can see why mocking frameworks are so valued and how Moq accomplishes its task. Post any questions in the comments below. I assure you that I read each and every one of them that same day.