Unit Test Session State in MVC using TestHelpers and Moq

Background

In my earlier post about Unit Test Session State in ASP.NET MVC I had demonstrated how to mock intrinsic object like Session state in ASP.NET MVC using Moq. The idea there was to abstract the access to SessionState out into separate class called SessionHelper and then use Moq to mock those method or property calls. In this post I would be using an elegant approach to achieve the same results using MVCContrib TestHelpers.

Using MVCContrib TestHelpers to mock intrinsic objects

The previous approach might be helpful if we are using only one or two intrinsic object within the controller actions. Most of the times we'll be using more than one of the ASP.NET MVC intrinsic object like
  • HttpContext
  • HttpRequest
  • HttpResponse
  • HttpSession
  • Form
  • TempData
  • QueryString
Abstracting each of these classes and their methods and properties can be very cumbersome process. But some smart people have already encountered such issues and have come up with a solution in the form of TestHelpers.
These TestHelpers use RhinoMocks internally to mock the calls to ASP.NET MVC intrinsic objects.
I have modified my example from the last post to suit the changes for TestHelpers. Another advantage of using these test helpers is that we get a set of extension methods which can be used to assert results in a fluent manner. I'll demonstrate that as well in the assert section of the tests.
Without wasting too much time let me dive into the code straight away. Here are the changes to the controller.
private readonly IUserRepository _userRepository;
//private SessionHelper _sessionHelper;

public LoginController() : this (new UserRepository())
{}

public LoginController (IUserRepository userRepository)
{
    _userRepository = userRepository;
    //_sessionHelper = sessionHelper;
}


As can be seen, I have removed the dependency on SessionHelper and inject the LoginController with IUserRepository. Based on these changes there is change in the LogOn action as well.
public ActionResult LogOn(FormCollection formCollection)
{
    string userName = formCollection["UserName"];
    string password = formCollection["Password"];

    bool validUser = _userRepository.ValidateUser(userName, password);

    if(validUser)
    {
        Session.Add("UserName",userName);
        return RedirectToAction("Index", "Home");
    }
    else
    {
        this.ModelState.AddModelError("InvalidUser", "Invalid user name or password");
        return View();   
    }
}

Please note the changes in bold and italics. I have used the HTTPSession object directly in the controller action. As discussed in earlier post this causes problems while unit testing this particular action. But TestControllerBuilder class from TestHelpers comes to the rescue. It takes the responsibility of handling intrinsic objects during unit testing. We can directly use those intrinsic objects in our production code as usual. Before calling the controller action from the unit test we need to set the values for intrinsic objects. This is handled by the InitializeController method of the TestControllerBuilder class as shown in the code snippets below
private Mock<IUserRepository> _mockUserRepository;
private TestControllerBuilder _builder;

private LoginController _loginController;

[SetUp]
public void SetUp()
{
    _mockUserRepository = new Mock<IUserRepository>();

    _builder = new TestControllerBuilder();

    _loginController = new LoginController(_mockUserRepository.Object);

    _builder.InitializeController(_loginController);
}
Please note the 2nd and 4th line above. This is all we need to do for setting up the intrinsic objects for controller action. Finally in my test I can assert that the Session variable was set properly.
[Test]
public void ValidUserDetailsAreStoredinSession()
{
    //Arrange
    string userName = "Nilesh";
    string password = "abc@123";

    FormCollection formCollection = new FormCollection();
    formCollection["UserName"] = userName;
    formCollection["Password"] = password;

    _mockUserRepository.Setup(userRepository => userRepository.ValidateUser(userName, password))
    .Returns(true);

     //Act
     ActionResult result =_loginController.LogOn(formCollection);

     //Assert
     //Assert.That(result.RouteValues["controller"], Is.EqualTo("Home"));
     //Assert.That(result.RouteValues["action"], Is.EqualTo("Index"));
     result.AssertActionRedirect()
           .ToController("Home")
           .ToAction("Index");

     Assert.That(_loginController.Session["UserName"], Is.EqualTo(userName));
}
You can see the changes highlighted above. I have used the AssertActionRedirect method to assert that Home controllers Index action is called. Finally I am asserting that UserName session value is same as what it was set to. This makes the code more readable and easy to understand as compared to earlier code which is commented out.

Conclusion

Using TestHelpers makes it very easy to unit test the intrinsic objects of ASP.NET MVC. Additional methods to assist during asserts also makes the code more readable and easier to understand. We no longer need to abstract all the intrinsic objects. The learning curve is also very less. We can also make use of these TestHelpers to write framework agnostic Asserts. This means that we need not depend on the syntax of the unit testing framework being used to Unit Test the code. I am sure I'll come across situations in my project to demonstrate the other objects like HTTPRequest, Form, QueryString etc over next few days.
Until next post happy programming :)

Note:

TestHelpers internally use RhinoMocks dll. make sure you copy that dll from which resides in the dependencies folder after MVCContrib files are extracted to the disk. Copy the RhinoMocks dll into same directory as MVCContrib.TestHelpers.dll.
You can download the complete source code from dropbox - SessionStateUnitTest_WithMVCTestHelpers.zip.

Share:
spacer

2 comments:

  1. Havе you evеr thought about including a little bit more than just youг articⅼes?
    I mean, what you say is important and all.
    However imagine if you added some great photos or video clips to give your posts more, "pop"!
    Your content is excellent but wіth imɑges and cliⲣs,
    thiѕ site could defіnitely be one of the best in itѕ
    field. Veгy good blog!
    i thoսght about this : Τimes Are Changing:
    How To Password Protect Folder New Skills

    ReplyDelete
  2. Excellent weblog right here! Also your web site quite a bit up very fast!
    What web host are you the use of? Can I am getting
    your associate link for your host? I want my website loaded up
    as quickly as yours lol

    ReplyDelete