JustMock is a great tool for abstracting dependencies in unit tests, and the new automocking feature makes it even faster to develop unit tests.  Another great feature in JustMock and JustMock Lite is the capability to assert the behavior of your system under test. 

Traditional TDD (Test Driven Testing) unit testing typically tests for state.  Did the user get logged in? Did the user’s shopping cart get loaded?  Important tests, of course.  But that only tests the end result of the method.  If the user does NOT successfully login, and the cart is not reloaded, is that because the call to the repository was never called? Or because some error happened that didn’t reload the cart in this particular use case?  The state of the application is correct, but is that because it executed the expected behavior, or because we got lucky? 

In addition to state based testing, another set of tests need to assert the behavior of the system under test and how it relates to its dependencies.  If a user failed to login, the cart repository should never be called.  Fortunately, with JustMock and JustMock Lite, this is very easy to accomplish!

Setting the Stage

The code that we will be testing is shown in Listing 1.  In the project, I have all of the classes in separate files, but for ease of blogging, I included them all in one listing.

using System;
  
namespace IntroToMocking.AssertingBehavior
{
    public class SecurityHandler
    {
        private readonly ILoginService _service;
        private readonly IShoppingCartRepository _cartRepo;
        private readonly ISecurityLogger _logger;
  
        public Cart ShoppingCart { get; internal set; }
        public int UserID { get; internal set; }
        public SecurityHandler(
            ILoginService service,
            IShoppingCartRepository cartRepo,
            ISecurityLogger logger)
        {
            _logger = logger;
            _cartRepo = cartRepo;
            _service = service;
        }
  
        public bool LoginUser(string userName, string password)
        {
            UserID = _service.ValidateUser(userName, password);
            if (UserID != 0)
            {
                ShoppingCart = _cartRepo.LoadCart(UserID);
                return true;
            }
            else
            {
                _logger.LogInvalidLogin(userName, password);
            }
            return false;
        }
    }
  
    public interface IShoppingCartRepository
    {
        Cart LoadCart(int userID);
    }
  
    public interface ISecurityLogger
    {
        void LogInvalidLogin(string userName, string password);
    }
  
    public interface ILoginService
    {
        int ValidateUser(string userName, string password);
    }
  
    public class Cart
    {
    }
}

Listing 1 – System Under Test and Dependencies

There are two behaviors that we want to assert.  The first is that the cart repository only attempts to reload the cart if the user successfully logs in.  The second is that the logger should only log an invalid login attempt when there is an invalid login (ok, that one seems so obvious now that I’ve typed it).

Asserting Occurrences of Calls

JustMock allows you to specify the number of times a mock method should be called in a variety of ways, as listed in Table 1.  In addition to specifying the occurrences, Assert() must be called on the mock.  By convention, this is done at the end of the test, after the other assert.

Specification  
MustBeCalled() Call must be executed at least one time
Occurs(x) Call must be executed exactly x number of times
OccursOnce() Call must be executed one time only (same as Occurs(1))
OccursNever() Call must not be executed (same as Occurs(0))
OccursAtLeast(x) Call must be executed at least x number of times
OccursAtMost(x) Call can be executed at most x number of times

Table 1 – Specifying Occurrences of method calls

Testing Interactions with the Cart Repository for Failed Login Attempts

The behavior that we expect from the system under test is to not call the cart repository when the login attempt fails.  This is done very simply by adding .Occurs(0) (or .OccursNever()) to the end of the arrange for the cart repository.  I’ve also added .Occurs(1) to the arrange for the ILoginService to verify that the system under test actually calls into the service to validate the user credentials.  The test is shown in Listing 2.  NOTE: When using automocking, calling Assert() on the container verifies all of the specifications for all of the mocks created by the automocking container.  If I didn’t use automocking in this test, I would have to specifically create a mock for all three dependencies, including the Logger that isn’t pertinent to this test.  I would then have to call Assert on the mocks for the ILoginService and the IShoppingCartRepository.

[Test]
public void Should_Not_Load_Cart_With_Invalid_Username_And_Password_Automocked()
{
    string userName = "Bob";
    string password = "Password";
    int userID = 0;
    Cart cart = new Cart();
    var container = new MockingContainer<SecurityHandler>();
    container.Arrange<ILoginService>(x =>
            x.ValidateUser(userName, password))
                .Returns(userID)
                .Occurs(1);
    container.Arrange<IShoppingCartRepository>(x =>
            x.LoadCart(userID))
                .Occurs(0);
    bool result = container.Instance.LoginUser(
            userName, password);
    Assert.IsFalse(result);
    Assert.AreEqual(container.Instance.UserID, userID);
    container.Assert();
}
Listing 2 – Testing the behavior of the login process

Asserting the Order of Calls

The final step in JustMock’s behavior testing is the ability to specify the order of execution for dependencies.  The ILogger logs invalid login attempts.  Logging an invalid login shouldn’t be executed until after the login actually fails.  To test for this is easy – simply add InOrder() to the arrangements.  The order is specified as the same order that the arrangements are listed in the test, and works across different mock objects.  The test for the behavior of Logging is shown in Listing 3.

[Test]
public void Should_Log_Invalid_Login_Attempts()
{
    string userName = "Bob";
    string password = "Password";
    int userID = 0;
    Cart cart = new Cart();
    var container = new MockingContainer<SecurityHandler>();
    container.Arrange<ILoginService>(x => 
        x.ValidateUser(userName, password))
                .Returns(userID)
                .InOrder()
                .Occurs(1);
    container.Arrange<ISecurityLogger>(x => 
        x.LogInvalidLogin(userName, password))
                .InOrder()
                .Occurs(1);
    bool result = container.Instance.LoginUser(userName, password);
    Assert.IsFalse(result);
    Assert.AreEqual(container.Instance.UserID, userID);
    container.Assert();
}
Listing 3 – Testing the order of execution

Summary

Asserting behavior is often overlooked when developers are testing their code, but automating the testing of code interaction is an extremely valuable exercise.  Not every test should include behavior checking, but there should be enough coverage to ensure the system under test is interacting with the dependencies as expected.

Happy Coding!

JustMock banner
Japikse
About the Author

Phil 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.

 

Related Posts

Comments