How does mocking benefit me? That’s a question I hear a lot as I travel around the world giving talks. So before we dive into how to use a mocking tool like JustMock Free Edition, let’s discuss why this is important and the benefit that mocking provides over other techniques like Fakes and Stubs.
As a primer, I encourage you to read “Mocks Aren’t Stubs” by Martin Fowler. Again, if you haven’t read it yet - go ahead. I’ll wait. Come back when you are done.
Finished it? Great. In my last post, we refactored a small piece of code to be more SOLID. As a refresher, here is where we ended up:
public class SecurityHandler
{
private readonly ILoginService _service;
public SecurityHandler(ILoginService service)
{
_service = service;
}
public bool LoginUser(string userName, string password)
{
return _service.ValidateUser(userName, password);
}
}
We externalized all of the dependencies (the login service) and made sure that the code is using an abstraction instead of the actual implementation. Among the other benefits we discussed in my last post, this insulates our code from depending on the developer of the LoginService to check in their code before we can verify our implementation. But this is a two-edged sword. Since we can’t instantiate an interface, we need to a concrete implementation to run this block of code.
Let’s add some additional functionality to make the code a bit more meaningful:
- When a user successfully logs into the system, the userid should be retrieved from the database and available to consumers of the SecurityHandler.
- When a user fails to login, the userid should be set to zero.
Our new SecurityHandler looks like this:
public class SecurityHandler
{
private readonly ILoginService _service;
public int UserID { get; internal set; }
public SecurityHandler(ILoginService service)
{
_service = service;
}
public bool LoginUser(string userName, string password)
{
return (UserID = _service.ValidateUser(userName, password)) != 0;
}
}
To execute this code in our tests, we have three options – create a Fake, a Stub or a Mock.
Fakes
A Fake is a class that we can use instead of the actual Line Of Business code. In this example, a fake implementation might look something like this:
public class FakeLoginService:ILoginService
{
public int ValidateUser(string userName, string password)
{
return 5;
}
}
This code doesn’t do anything real. It is hard coded to return true. But it does provide two important benefits:
- It allows us to write our first unit test.
- It eliminates the need for a real Login Service, isolating our code from the noise of integration issues.
For this test, we first create an instance of the SecurityHandler, injecting our Fake in for the Service dependency. We then execute the LoginUser method. Since we know the Fake returns a non-zero integer, we expect our method to return true, and that the UserID property of the handler is correctly set to the returned ID from the LoginService. NOTE: I am using NUnit for these examples, but any of the common unit testing frameworks will do.
[Test]
public void Should_Return_True_With_Valid_Username_And_Password()
{
SecurityHandler handler = new SecurityHandler(new FakeLoginService());
Assert.IsTrue(handler.LoginUser(string.Empty, string.Empty));
Assert.AreEqual(handler.UserID, 5);
}
Looking at this test, it validates the “happy path” of our security handler. If we use a code coverage tool, we could also validate that we have completely covered our Line of Business code with our unit test. There is another extremely important concept with unit testing in addition to code coverage, and that is use case coverage. This test does not cover the situation where the user has entered an invalid userid/password combination.
In order to do this with a fake, we would need to write another class (perhaps called FakeFailingLoginService) and create another unit test to do verify that our code successfully handles the “unhappy” path. I’m all for creating another test to test a different execution path, but creating another implementation to hard code a failing login is too much friction. We have better things to do with our time than write repetitive code.
Which brings us to:
Stubs
Martin Fowler defines Stubs as objects that provide canned answers to calls made during the test. This might seem the same as the Fake that we wrote above, but the biggest difference is that we can use a mocking framework like JustMock to create the Stub in the test, providing the necessary scaffolding for the system under test in very little code.
To do this, we create a Loose Mock, arrange for how that object should behave when called, and pass that into the SecurityHandler. The way this happens is that the JustMock framework creates a proxy that wraps the interface, allowing us to treat it like a real implementation of the interface.
To demonstrate this, we can delete the FakeLoginService from our solution, and rewrite the test as follows:
1: [Test]
2: public void Should_Return_True_With_Valid_Username_And_Password()
3: {
4: string userName = "Bob";
5: string password = "Password";
6: ILoginService service = Mock.Create<ILoginService>();
7: Mock.Arrange(() => service.ValidateUser(userName, password)).Returns(5);
8: SecurityHandler handler = new SecurityHandler(service);
9: Assert.IsTrue(handler.LoginUser(userName, password));
10: Assert.AreEqual(handler.UserID, 5);
11: }
What exactly is going on here? On line 6, we are creating the proxy for the LoginService interface. This is an important first step, as we can’t instantiate an interface. Line 7 replaces the need for the Fake that we created above. Here we are
arranging how the stub will behave when called. It says “When the method ValidateUser is called on the service object and the parameters ‘Bob’ and ‘Password’ are passed in, return the number 5”.
This proxy is then passed in as the dependency for the SecurityHandler. When LoginUser is called on the handler, it executes the arranged method on the proxy in this line:
return (UserID = _service.ValidateUser(userName, password)) != 0;
Everything works as expected, the test passes, and we’re done, right? Not yet. One of the drawbacks of using Fakes was the extra code needed to cover the various use cases. With Stubs, this becomes trivial as the following test shows:
[Test]
public void Should_Return_False_With_Invalid_Username_And_Password()
{
string userName = "Bob";
string password = "Password";
ILoginService service = Mock.Create<ILoginService>();
Mock.Arrange(() => service.ValidateUser(userName, password)).Returns(0);
SecurityHandler handler = new SecurityHandler(service);
Assert.IsFalse(handler.LoginUser(userName, password));
Assert.AreEqual(handler.UserID, 0);
}
The requirements state that if the user fails to login, the service should return a zero. The security handler then reflects that failure by setting the UserID property to zero as well.
In JustMock terms, a Stub is a Loose Mock, and is the default type of proxy created. Any method on the Stub that gets called but wasn’t specifically arranged will still succeed, returning the default value for the return type. In our example, if we didn’t arrange the call to ValidateUser, the call would return a zero, the default value for the integer data type.
We could have written our failing login test without the arrange (see the code sample below), but that would certainly make the code less readable. The reader would have to know what the default behavior is for a method that isn’t arranged.
[Test]
public void Should_Return_False_With_Invalid_Username_And_Password()
{
string userName = "Bob";
string password = "Password";
ILoginService service = Mock.Create<ILoginService>();
SecurityHandler handler = new SecurityHandler(service);
Assert.IsFalse(handler.LoginUser(userName, password));
Assert.AreEqual(handler.UserID, 0);
}
An important facet of unit testing in addition to validating the Line of Business code is to provide a mechanism for another developer to be able to quickly understand what the code does (and doesn’t) do, and to convey the “spirit” of the code.
We can certainly see the benefits of using a Stub over a Fake, but one shortfall for Stubs is the lack of support for Behavior Specification. I caught (and unfortunately written) my share of bugs where there was an assumption that a block of code was being executed when in fact it wasn’t.
For example, what if we modify the SecurityHandler to the following code block:
public class SecurityHandler
{
private readonly ILoginService _service;
public int UserID { get; internal set; }
public SecurityHandler(ILoginService service)
{
}
public bool LoginUser(string userName, string password)
{
UserID = 5;
return true;
//return (UserID = _service.ValidateUser(userName, password)) != 0;
}
}
Don’t laugh. I once had a developer do this because the service wasn’t ready, and he wanted to check his code in. This code made it all of the way to QA! (This particular developer hadn’t written a single unit test either.)
We need a way to validate that the ValidateUser method of the LoginService object actually gets called. And we can’t do that with a traditional Stub.
Mocks
Mocks bring all of the benefits of Stubs plus the ability to specify behavior. To demonstrate this, we are going add behavior checking into the test. The first change we make is to verify that the arranged method was actually called. To do this with JustMock, we make some minor changes to our test. As you can see in the following code sample, the keyword Occurs(1) is added to the end of the call to Mock.Arrange. (Note: There are several forms of this call, which we will cover in a later blog post.) This creates the expectation that this method will be called once (and only once).
The second change to the test is that we add the line Mock.Assert(service). The call to Mock.Assert tells the JustMock framework to check all expectations, and throw an exception if everything didn’t occur exactly as arranged.
[Test]
public void Should_Return_True_With_Valid_Username_And_Password()
{
string userName = "Bob";
string password = "Password";
ILoginService service = Mock.Create<ILoginService>();
Mock.Arrange(() => service.ValidateUser(userName, password)).Returns(5).Occurs(1);
SecurityHandler handler = new SecurityHandler(service);
Assert.IsTrue(handler.LoginUser(userName, password));
Assert.AreEqual(handler.UserID, 5);
Mock.Assert(service);
}
If the ValidateUser method is never called, the above test will fail with the message:
Telerik.JustMock.MockAssertionException : Expected call on the mock should occur exactly 1, but it was called 0 time(s).
JustMock actually provides the Occurance checking capability with Loose Mocks. But to fully validate the behavior of a mocked object, we need to use what is known as a Strict Mock. Remember that any methods that are not specifically arranged with a Loose Mock will just return the default value? What we want is to have an exception thrown (and therefore fail the test) if any method is called that was not arranged.
To create a Strict Mock you simply add the parameter Behavior.Strict Mock.Create as the code snippet below demonstrates.
ILoginService service = Mock.Create<ILoginService>(Behavior.Strict);
To show the different behavior of the different types of Mocks, let’s go back to the version of the unhappy path test that didn’t include the arrange. If we change the call to Mock.Create to create a Strict Mock, and run the test again, we will get a failed test with the following message:
Telerik.JustMock.MockException : All calls on the mocking object should be arranged first.
This enables us to validate not only that what we expected to be called was called, but also catches unwanted side effect calls that we didn’t realize were occurring.
Summary
When it comes to testing, we need to make sure that we are thoroughly executing our Line of Business code. But we also have to keep in mind that if testing adds too much friction to the process, it just won’t happen. By using SOLID development practices, and JustMock to handle the dependencies and behavior verification, a significant amount of friction simply goes away.
Happy Coding!