Abstracting Dependencies with JustMock

Unit tests are most effective when the System Under Test is isolated from its dependencies. The best way to isolate your code (and make your code more SOLID) is through dependency injection. But even if you follow the rules of SOLID and inject all of the necessary dependencies in as interfaces, you are still faced with the fact that you depend on them.

In order to test your code, something concrete has to be passed into the code in order for it to even compile. Your production code will most likely use a factory to create concrete instances of the dependent interfaces, but that is a lot of overhead for a simple unit test.

This is where mocking comes in to save the day. With very few lines of code, you can create concrete classes (also known as proxies) around your interfaces. You can also arrange the behavior of these proxies in order to force the conditions that you want to test.

The code in Listing 1 represents a simple security handler (this may look familiar to you if you have been following this series of blog posts) that has three dependencies:

IUserValidationService

ILoggingService

ICartRepository

All three of them are injected into the class through constructor injection (highlighted in yellow).

publicclass SecurityHandler

{

public IList<string> Cart { get; privateset; }

privatereadonly IUserValidationService _validationService;

privatereadonly ILoggingService _logger;

privatereadonly ICartRepository _cartrepo;

public SecurityHandler(

IUserValidationService validationService,

ILoggingService loggingService,

ICartRepository cartrepo)

{

_cartrepo = cartrepo;

_logger = loggingService;

_validationService = validationService;

}

publicbool LoginUser(string userName, string password)

{

var userID = _validationService.ValidateUser(

userName, password);

if (userID == 0)

{

_logger.LogFailedLogin(userName, password);

returnfalse;

}

Cart = _cartrepo.LoadCart(userID);

return (userID != 0);

}

}

Listing 1

For the sake of reference, the interfaces are shown in Listing 2.

publicinterface ICartRepository

{

IList<string> LoadCart(int userID);

}

publicinterface ILoggingService

{

void LogFailedLogin(string userName, string password);

}

publicinterface IUserValidationService

{

int ValidateUser(string userName, string password);

}

Listing 2

The first two tests check that the LoginUser method calls into the IUserValidationService and returns true if the UserValidationService returns a positive integer.

Setting up the test is very straightforward, or so it seems. I created four local variables, one each for the username and password, one for the userid and the final one for the SecurityHandler instance (traditionally named “sut” for system under test).

The beginning of the test is shown in Listing 3. It will not compile because there isn’t a no argument constructor for the SecurityHandler class.

[TestFixture]

publicclass SecurityHandlerTests

{

[Test]

publicvoid ShouldLoginUserWithValidUserNamePassword()

{

var userName = "Bob";

var password = "Password";

var userID = 5;

//This won't compile - missing dependencies

var sut = new SecurityHandler();

var actual = sut.LoginUser(userName, password);

Assert.IsTrue(actual);

}

}

Listing 3

To get this code to compile, we need to create the three dependencies. I can do this with one line of code for each of the interfaces using JustMock, and then pass these mock objects (the proxies) into the SecurityHandler constructor, as shown in Listing 4. (For more information on unit testing with JustMock and NUnit, please refer to my blog post on Getting Started with JustMock and NUnit.)

//Create Mock User Validation Service

var mockUserValidation = Mock.Create<IUserValidationService>();

//Create Mock Logger

var mockLogger = Mock.Create<ILoggingService>();

//Create Mock Cart Repository

var mockCartRepository = Mock.Create<ICartRepository>();

var sut = new SecurityHandler(

mockUserValidation, mockLogger, mockCartRepository);

Listing 4

When we run this test, it fails. Not because of a compile error or runtime error, but because the call to ValidateUser on the mocked UserValidation service returns a zero. This is by design in JustMock - any call on a mock that is not arranged returns the default value for the return type. In this case, the datatype of the return value of the ValidateUser method returns an integer, and thus defaults to zero.

Based on our trivial logic, the LoginUser method will return true if the ValidateUser returns a number greater than zero. Since the call to ValidateUser was not arranged, it returns the default (zero) and the test fails.

NOTE: This is the default behavior for method calls that are not arranged on loose mocks. Strict mocks behave differently, as I will show you in a later blog post.

Dependencies that affect the outcome of our test need to have the appropriate methods arranged. This tells the mocking framework how the proxy should behave when called, and JustMock again makes this very easy. The code is shown in Listing 5.

//Arrange the behavior if ValidateUser is called

mockUserValidation.Arrange(x => x.ValidateUser(

userName, password))

.Returns(userID);

Listing 5

Now when we run the test, the mock of the IUserValidation service returns the value of the variable userID (in our test it is set to 5).

Now our test passes, and all we had to do was add 4 lines of code to abstract the dependencies away from the system under test! The final test is shown in Listing 6.

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.