Why Mocking Matters

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, let’s discuss why this is important and the benefit that mocking provides over other techniques like Fakes.

In my post on Why SOLID Matters, we refactored a small piece of code to be more SOLID. Granted, it was a simplistic example, but good for illustrating several key points on SOLID. We will continue using the same code to discuss the benefits of Mocking.

As a refresher, we ended up with the class in Listing 1.

publicclass SecurityHandler

{

ILoginService _service;

public SecurityHandler(ILoginService service)

{

_service = service;

}

publicbool LoginUser(string userName, string password)

{

return _service.ValidateUser(userName, password);

}

}

Listing 1

We externalized the dependency (the login service) and made sure that the code is using an abstraction instead of the actual implementation. We no longer depend on the developer of the LoginService to check in his/her code before we can build our project. However, since we can’t instantiate an interface, there isn’t any way for us to run this code to test our implementation of the business logic. NOTE: This business logic is extremely trivial, so we are going to modify the SecurityHandler class to improve the example (just a bit). We change the ValidateUser call to return an integer (the UserID), and if it returns anything but zero, then the user has successfully logged in.

publicclass SecurityHandler

{

ILoginService _service;

public SecurityHandler(ILoginService service)

{

_service = service;

}

publicbool LoginUser(string userName, string password)

{

return (_service.ValidateUser(userName, password) != 0);

}

}

Listing 2

In order to test this business logic, and be able to execute 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. Fakes implement the same interface as the dependencies that they are standing in for, but have hard coded results and don’t interact with any other objects or services. If the dependency is a database access item (like a repository), the FakeRepository doesn’t actually talk to the database, but is hardwired to return a collection of items. For our example, we will return 5 if the UserName = “Bob” and the Password = “password”, and 0 under any other scenario. The code is shown in Listing 3.

publicclass FakeLoginService: ILoginService

{

publicint ValidateUser(string userName, string password)

{

if (userName == "Bob" && password == "password")

{

return 5;

}

return 0;

}

}

Listing 3

Even though we only have two options (return 5 for success or 0 for failure), 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.

To test this, we create our first unit test. If you are new to unit testing. please review my post on Getting Started With Nunit, NunitShould, JustMock to see how to quickly get started with NUnit unit tests. In the Arrange section of our unit test, we create an instance of the SecurityHandler class and pass in the FakeLogin service into the constructor. We then call LoginUser (our Action), and then Assert that the user successfully logged in. The full test is shown in Listing 4.

[TestFixture]

publicclass SecurityHandlerTests

{

[Test]

publicvoid ShouldLoginUserIfUserIdIsNotZero()

{

FakeLoginService fake = new FakeLoginService();

SecurityHandler sut = new SecurityHandler(fake);

bool result = sut.LoginUser("Bob", "Password");

Assert.IsTrue(result);

}

}

Listing 4

Since we know the Fake returns a 5 when passing in the username and password, we expect the test to pass. But it fails, and looking at the test, it isn’t clear why it failed. When we look deeper into the issue, we see that the Fake is expecting “password” but our test is using “Password”.

This illustrates the first problem with using Fakes. They add friction to understanding the tests because the tests are no longer self contained. We must look at multiple files to understand how the test works (and why it didn’t if it fails).

The second problem is our Fake only returns 5 or 0 (zero). What if we want to test our business logic to see how it will handle a negative return? We need to add another “else” statement to handle that case, and somehow remember the different cases when we are writing our tests. And this is trivial example. What if the Fake was representing a data repository, and we wanted to return various length ILists based on certain parameters so we can test each use case? Our tests quickly become difficult to understand and maintain, and the usual course of action is for developers to abandon testing at this point.

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 Lite to create the Stub in the test, providing the necessary scaffolding for the system under test with very little code. And it is completely evident to a developer reading the test what is being tested and how the dependency will react, and therefore makes it clear how the system under test should behave.

When created with a mocking framework, a Stub is a lightweight proxy wrapped around another object (in our current example, we are wrapping an interface). This proxy than has expectations set on it, such as how it will behave when different methods on the interface are invoked. Now that we have a concrete object, we can inject the proxy into the system under test fulfilling the dependency requirements.

The default type of mock created by JustMock is a Loose mock. A Loose mock is the closest implementation to a Stub that JustMock provides, and behaves differently than a Strict mock (which will be discussed in the Mocks section later in this post).

When a method is called on a Loose mock that has not been arranged, the method will return the default value for the return type (i.e. 0 (zero) for a numeric type, null for an object, etc.) and allow the system under test to continue on its way.

Our first test using a Stub is shown in Listing 5.

[Test]

publicvoid ShouldLoginUserIfUserIdIsNotZeroUsingStub()

{

var mock = Mock.Create<ILoginService>();

string userName = "Bob";

string password = "Password";

mock.Arrange(x => x.ValidateUser(userName, password))

.IgnoreArguments().Returns(5);

SecurityHandler sut = new SecurityHandler(mock);

bool result = sut.LoginUser(userName, password);

Assert.IsTrue(result);

}

Listing 5

What exactly is going on here? On the first line of the test, we are creating the Mock. On the next two lines, we create (and assign the values for) the variables userName and password. The most interesting line is the next line (shown in Listing 6), where we are Arranging the behavior of the stub. The lambda expression contains the method that will be called, as well as the parameters to be passed in. The next statement (IgnoreArguments()) tells the stub that it doesn’t matter what values are passed into the method call, it should always behave exactly the same. Lastly, the Stub will return 5.

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 in Listing 7 shows.

[Test]

publicvoid ShouldNotLoginUserIfUserIdIsZeroUsingStub()

{

var mock = Mock.Create<ILoginService>();

string userName = "Bob";

string password = "Password";

mock.Arrange(x => x.ValidateUser(userName, password))

.IgnoreArguments().Returns(0);

SecurityHandler sut = new SecurityHandler(mock);

bool result = sut.LoginUser(userName, password);

Assert.IsFalse(result);

}

Listing 7

By changing the value in the “Return()” call to 0 (zero), the Stub replicates a failed login. The requirements state that if the user fails to login, the service should return a zero. And we can assert that the result of the login attempt is false.

Since the behavior for a Loose mock (the method for creating a stub in JustMock) is to return the default value for the return type, we could have created the failing login test without specifying an arrange on the stub. While this is not technically incorrect, it does make the test harder to read. Especially if you aren’t familiar with the default behaviors.

It is very important when developing software (whether you are writing unit tests or line of business code) to make sure that the code is clear and concise.

Clearly, stubs have a great deal of benefit over using Fakes. Not only are your tests more readable, but there is a lot less code that needs to be written. By reducing the amount of code, it is more likely that tests will be written and maintained.

Mocks

Even though stubs add a great deal of benefit over fakes, they can’t verify the behavior of the system under test. Not every test should verify how your code interacts with its dependencies, but there should be at least a few tests that do. This allows your automated tests to catch bugs where there was an assumption that code was being executed when in fact it wasn’t, or vice versa.

For example, what if we modify the SecurityHandler LoginUser method to the code block in Listing 8, all of our tests will still pass. But we are making the exact same call twice, which can severely hamper performance (or cost money, e.g. credit card processing).

publicbool LoginUser(string userName, string password)

{

var userID = _service.ValidateUser(userName, password);

return (_service.ValidateUser(userName, password) != 0);

}

Listing 8

We need a way to validate that the ValidateUser method of the LoginService object gets called one time and one time only. (I’ve also seen instances where developers didn’t want to wait for the service class to be written, didn’t understand Dependency Inversion, and hardcoded a result – like return 5.) And we can’t do that with a traditional Stub.

Mocks bring all of the benefits of Stubs plus the ability to specify behavior by specifying an expectation on how many times a method must be executed. To do this with JustMock, the keyword Occurs(1) is added to the end of the call to Mock.Arrange. This creates the specification that this method will be called once (and only once). Note: There are several forms of this call, which we will cover in a later blog post.

The second change to the test is that we add the line mock.Assert(). The call to Assert tells the JustMock framework to check all expectations, and throw an exception if everything didn’t occur exactly as arranged. The changes are highlighted in Listing 9.

[Test]

publicvoid ShouldLoginUserIfUserIdIsNotZeroUsingStub()

{

var mock = Mock.Create<ILoginService>();

string userName = "Bob";

string password = "Password";

mock.Arrange(x => x.ValidateUser(userName, password))

.IgnoreArguments().Returns(5).Occurs(1);

SecurityHandler sut = new SecurityHandler(mock);

bool result = sut.LoginUser(userName, password);

Assert.IsTrue(result);

mock.Assert();

}

Listing 9

When the test is executed against the code that executes ValidateUser twice, we get the following error message:

Error Message:
Expected ILoginService.ValidateUser(String,String) call on the mock should occur exactly 1, but it was called 2 time(s).

In addition to providing checking for the number of times a method gets executed through the Occurs method, JustMock also offers Strict Mocks, Remember that any methods that are not specifically arranged with a Loose Mock will just return the default value? Strict Mocks will throw 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.

var mock = Mock.Create<ILoginService>(Behavior.Strict)

I will show the full difference between Loose and Strict Mocks in a later post, but to sum it up, if anything gets called on a Strict mock that wasn’t arranged, JustMock will throw an exception similar to this:

Telerik.JustMock.MockException : All calls on the mocking object should be arranged first.

This adds another layer to the behavior checking capabilities, and any unexpected calls on your mocked object will cause the test to fail.

Stubs vs. Mocks

There is a lot of debate between when you use Stubs vs Mocks, well really about what level of behavior checking you should add into your tests via your mocking framework. The trick is to have enough so that you catch bugs where expectations aren’t being met, but not so much as to make your tests brittle.

To start out, I recommend more rather than less. As you gain more experience, you can dial down how much you are doing through your mocks. I feel that it is better safe than sorry.

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 a mocking framework like JustMock Lite to handle the dependencies and behavior verification, a significant amount of friction simply goes away.

Phil Japikse

Philip Japikse is an international speaker, a Microsoft MVP, ASPInsider, INETA Community Champion, MCSD, CSM/ CSP, and a passionate member of the developer community. Phil has been working with .Net since the first betas, developing software for over 20 years, and heavily involved in the agile community since 2005. Phil also hosts the Hallway Conversations podcast (www.hallwayconversations.com) and serves as the Lead Director for the Cincinnati .Net User’s Group (http://www.cinnug.org). You can follow Phil on twitter via www.twitter.com/skimedic, or read his personal blog at www.skimedic.com/blog.

Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks for appropriate markings.