MVC Series Part 2: AccountController Testing

Introduction

Click here to view source code

In my first post of the series, I explained the perils and pitfalls that I had to overcome with dynamically adding items. One of the next problem I ran into was dealing with unit testing the AccountController. More specifically, attempting to represent the UserManager class. Since unit testing is a fundamental necessity for any server project, testing the controller was a necessity.

Attempting to Test

So, let’s first create a test class for the AccountController and include a simple test for determining if a user was registered. Here is how my class first appeared:

[TestClass]
public class AccountControllerTest
{
    [TestMethod]
    public void AccountController_Register_UserRegistered()
    {
        var accountController = new AccountController();
        var registerViewModel = new RegisterViewModel
        {
            Email = "test@test.com",
            Password = "123456"
        };
        var result = accountController.Register(registerViewModel).Result;
        Assert.IsTrue(result is RedirectToRouteResult);
        Assert.IsTrue( _accountController.ModelState.All(kvp => kvp.Key != "") );
    }
}

When running the unit test I get a NullReferenceException thrown when attempting to access the UserManager. At first I assumed this was due to not having a UserManager created, but debugging at the location of the thrown exception led me to this:

ApplicationUserManager UserManager
{
     get
     {
          return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
     }
     private set
     {
          _userManager = value;
     }
}

The exception is actually getting thrown on the HttpContext property that is part of ASP.Net internals. We cannot assign HttpContext directly on a controller since it is read-only, but the ControllerContext on it is not, which explains how to do that here. We can create this easily enough by installing the Moq NuGet package to help mock this out. We will install the package and place the initialization of our AccountController into a initialize test class that will get called prior to every unit test:

private AccountController _acocuntController;
[TestInitialize]
public void Initialization()
{
     var request = new Mock<HttpRequestBase>();
     request.Expect( r => r.HttpMethod ).Returns( "GET" );
     var mockHttpContext = new Mock<HttpContextBase>();
     mockHttpContext.Expect( c => c.Request ).Returns( request.Object );
     var mockControllerContext = new ControllerContext( mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object );
     _acocuntController = new AccountController
     {
          ControllerContext = mockControllerContext
     };
}

Now when we run our application we no longer have to worry about the HttpContext, but still there is another NullReferenceException being thrown. This time it is from the call to ‘GetOwinContext’.

Alternative Route

At this point, attempting to mock out all of HttpContext’s features seems like a never ending road. All we really want is the ability to use UserManager’s feature to register a user. In order for us to do that we will need to mock out the IAuthenticationManager. This is no easy feat considering how well embedded the UserManager is within the AccountController. Fortunately, a post mentioned here mentions the right direction for substituting the ApplicationUserManager.

What we want to do is create a new class, called AccountManager, that will act as an access to the UserManager. The AccountManager will take in an IAuthenticationManager and also a IdentityDbContext, in casewe need to specify the specific context. I decided to place this class in a separate library that both the MVC and unit test libraries can access. If you decide to do the same and copy the class from the sample project, most of the dependencies will get resolved except for the HttpContextBase extension ‘GetOwinContext’. The reason is because that extension needs Microsoft.Owin.Host.SystemWeb. You can simply install this dependency in your library as a Nuget package through this command:

  • Install-Package Microsoft.Owin.Host.SystemWeb

Now that we have our AccountManager, we need to make sure our AccountController will use this class rather than attempting to create the UserManager from HttpContext. This starts with the constructor, where now we will have it accept our manager rather than passing in a UserManager:

public AccountController( AccountManager<ApplicationUserManager, ApplicationDbContext, ApplicationUser> manager)
{
    _manager = manager;
}

Then we will change the access to AccountController.UserManager to use the AccountManager:

public ApplicationUserManager UserManager
{
    get
    {
        return _manager.UserManager;
    }
}

Dependency Injection

Now the immediate problem with this is that MVC’s controllers are stateless and handle the creation of all the classes, including any objects that are injected into the class. Fortunately, Unity has dependency injection specifically for MVC that will allow us to inject our own objects. As of this writing, I went ahead and installed Unity’s MVC 5, which is referenced here. It’s a very seamless process to integrate Unity into your MVC project. After installing the package, open the Global.asax.cs, where your Application_Start() method is stored and add in ‘UnityConfig.RegisterComponents();’. Afterwards, in the App_Start folder, open the UnityConfig.cs file and register our AccountManager:

container.RegisterType<AccountManager<ApplicationUserManager, ApplicationDbContext, ApplicationUser>>(new InjectionConstructor());

We will also need to override our initialization process for the AccountController to ensure the AccountManager either gets the embedded HttpContext from the AccountController or one we provide during test:

protected override void Initialize( RequestContext requestContext )
{
    base.Initialize( requestContext );
    _manager.Initialize( HttpContext );
}

We will also need to remove the references to AuthenticationManager and instead have our AccountController reference the AccountManager’s AuthenticationManager. This will also cause our SignInAsync method to this:

private async Task SignInAsync( ApplicationUser user, bool isPersistent )
{
    await _manager.SignInAsync( user, isPersistent );
}

Mocking AccountController

Now we can run our application and register a user using our AccountManager. With this implementation in place, we simply need to mock out our IAuthenticationManager. Here is a post that describes a bit of the process. So, following suit, we go ahead and mock out the necessary classes for initializing up our test AccountController, all under the same Initialization class:

private AccountController _accountController;
[TestInitialize]
public void Initialization()
{
    // mocking HttpContext
    var request = new Mock<HttpRequestBase>();
    request.Expect( r => r.HttpMethod ).Returns( "GET" );
    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Expect( c => c.Request ).Returns( request.Object );
    var mockControllerContext = new ControllerContext( mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object );
    // mocking IAuthenticationManager
    var authDbContext = new ApplicationDbContext();
    var mockAuthenticationManager = new Mock<IAuthenticationManager>();
    mockAuthenticationManager.Setup( am => am.SignOut() );
    mockAuthenticationManager.Setup( am => am.SignIn() );
    var mockUrl = new Mock<UrlHelper>();
        var manager = new AccountManager<ApplicationUserManager, ApplicationDbContext, ApplicationUser>( authDbContext, mockAuthenticationManager.Object );
    _accountController = new AccountController( manager )
    {
        Url = mockUrl.Object,
        ControllerContext = mockControllerContext
    };
    // using our mocked HttpContext
    _accountController.AccountManager.Initialize( _accountController.HttpContext );
}

Now we can effectively test our AccountController’s logic. It’s unfortunate this process was anything but straight forward, but at least we are able to have better unit test code coverage over our project.

MVC Series Part 1: Dynamically Adding Items Part 1

Introduction

Recently, I have been exposed to working on a project using ASP.NET MVC 5. My experience working with MVC for the first time was overall positive, but it did run into quite a few hair pulling scenarios. My background up to this point primarily dealt with C# WPF applications, but with a little bit of web development on the side.  In this series, I will discuss some of the major issues that led to many sleepless nights working on this project. And although these scenarios may not seem to be the most difficult to an experienced MVC developer, these are the problems that caused major hiccups to a first timer like myself.

Dynamically Adding Items

Click here to view the source code

In one of the first major issues I needed to attempt to solve was trying to add items dynamically to a View. Representing a collection already populated is fairly straight forward using MVC’s Razor, as represented here in my collection of classical books:

<dlclass="dl-horizontal"id="booksContainer">
        @foreach ( var classicBook in @Model.ClassicBooks )
        {
            <dlclass="dl-horizontal">
                <dt>
                    @Html.DisplayNameFor( model => classicBook.Title )
                </dt>
                <dd>
                    @Html.DisplayFor( model => classicBook.Title )
                </dd>
                <dt>
                    @Html.DisplayNameFor( model => classicBook.Author )
                </dt>
                <dd>
                    @Html.DisplayFor( model => classicBook.Author )
                </dd>
            </dl>
        }
</dl>

We can even go one step further and place this inside a ‘DisplayTemplate’ and push the display content into a partial view:

dynamic-img-1

<dl class="dl-horizontal" id="booksContainer">
        @foreach ( var classicBook in @Model.ClassicBooks )
        {
            @Html.DisplayFor(model => classicBook);
        }
</dl>

But how do we add content on the fly?

Well, we could have a button that inserts javascript into our ‘booksContainer’ element and this would provide a seamless experience for the user. But what if we decide to change how the view looks? And how could we get this to post back to MVC server? What about if we decided to take in an image file as well?

Given I did not want to update the view in two places, I managed to find a solution that managed to do all of the above, linked here.

In summary, I was able to use an ajax call to retrieve my BookViewModel’s partial html view from the server and inject it into an html element:

  • Adding an editor partial view for the BookViewModel

dynamic-img2

 

  • Including the html element that will take in new books:
    <div id="newBooks">
        @for ( int i = 0; i < @Model.NewBooks.Count(); i++ )
        {
            @Html.EditorFor( model => @Model.NewBooks[i] )
        }
    </div>
  • On the server, the HomeController’s method that simply returns my partial view:
    public ActionResult CreateNewBook()
    {
        var bookViewModel = new BookViewModel();
        return PartialView( "~/Views/Shared/EditorTemplates/BookViewModel.cshtml", bookViewModel );
    }
  • The ajax call that handles the add book button click and injects the returned partial view:
    @section Scripts {
        <script type="text/javascript">
            $("#addbook").on('click', function () {
                $.ajax({
                    async: false,
                    url: '/Home/CreateNewBook'
                }).success(function (partialView) {
                    $('#newBooks').append(partialView);
                });
            });
        </script>
    }

And now we can dynamically add items on button click! The user is able to add as many books as they want, click submit, and the collection of books will get received on HttpPost. But, when we put a break post on our post method, the NewBooks collection comes back as empty. Why is this happening?

Well the reason is because MVC can only bind back a collection of items if it can uniquely identify each one. This article actually describes how you can get a collection of items to bind appropriately on post back, linked here.

Unfortunately, this does not apply when items are getting added dynamically. Luckily, the previous article also solved this problem by creating a Html Helper class, BeginCollectionItem, that adds a unique identifier to every new inserted item. All we need to do is modify our BookViewModel editor template to include it:

@using ( Html.BeginCollectionItem( "NewBooks" ) )
{
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor( model => @Model.Title )
        </dt>
        <dd>
            @Html.EditorFor( model => @Model.Title )
        </dd>
        <dt>
            @Html.DisplayNameFor( model => @Model.Author )
        </dt>
        <dd>
            @Html.EditorFor( model => @Model.Author )
        </dd>
        <dt>
            @Html.DisplayName( "Book Cover File Image" )
        </dt>
        <dd>
            @Html.TextBoxFor( m => @Model.BookCoverUrl, null, new { type = "file", @class = "input-file" } )
        </dd>
    </dl>
}

And with that we can now dynamically add items and they will uniquely get posted back onto the server.

Ajax.ActionLink

Click here to view the updated source code with the Ajax.ActionLink button

To add to this there is also a way create a new book using Ajax.ActionLink.

To demonstrate this the change involves removing the original ‘input’ button to this Razor Ajax button:

@Ajax.ActionLink( “Add Book”, “CreateNewBook”, new AjaxOptions
{
HttpMethod = “GET”,
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = “newBooks”
} )

Behind the scenes, this is how the button is represented as straight HTML:

a data-ajax=”true” data-ajax-method=”GET” data-ajax-mode=”after” data-ajax-update=”#newBooks” href=”/Home/CreateNewBook” rel=”nofollow”>Add Book

This also allows us to remove all the scripting for the Index.cshtml page, but I was still having issues clicking the button without it redirecting me to a new page. Then I stumbled upon this article (scroll down to the ‘Ajax.ActionLink’ section), which mentioned including a jquery.unobtrusive-ajax.js file. This script will interrupt the submission process for items that have the data-ajax attribute, which we have seen Razor creates such an element when it renders to the web page.