Extending Steve Sanderson’s ContextMocks class
July 27, 2009I’ve been working on unit testing ASP.NET MVC Controller actions and have been using the ContextMocks and other fake classes Steve Sanderson provided in his excellent book, Pro ASP.NET MVC Framework. It’s literally under 50 lines of code and uses Moq to supply just enough context to your controller actions so you can test without fireworks.
Thus far I’ve had to extend Steve’s solution twice: Ability to set and access User.Identity.Name (username of the authenticated user) and the ability to set and access the HttpMethod (GET, POST…). If you’ve been following along the book and need to take advantage of these properties in your unit tests, then this post is for you. Note: I will not be including full source code – only the code I’ve added. Unfortunately this may not make complete sense if you aren’t aware of Steve’s implementation.
Setting the User.Identity.Name
Specifying the User.Identity.Name is done just like the other Setup methods on HttpContext, something you could either do right in your ContextMocks constructor, or in a separate method like the one below:
public void SetupContextUser(string UserName)
{
HttpContext.Setup(x => x.User).Returns(new FakePrincipal(new FakeIdentity(UserName)));
}
This should be pretty straightforward except for the FakePrincipal and FakeIdentity… what’s that all about? The Identity in User.Identity.Name implements the interface IIdentity, and the Name in User.Identity.Name implements IPrincipal. While perhaps not the only option, what I ended up doing is creating a fake class for each, just implementing the bare minimum:
private class FakePrincipal : IPrincipal
{
private FakeIdentity _ident;
public FakePrincipal(FakeIdentity ident)
{
_ident = ident;
}
#region IPrincipal Members
public IIdentity Identity
{
get { return _ident; }
}
public bool IsInRole(string role)
{
throw new NotImplementedException();
}
#endregion
}
private class FakeIdentity : IIdentity
{
private string _name;
public FakeIdentity(string Name)
{
_name = Name;
}
#region IIdentity Members
public string AuthenticationType
{
get { throw new NotImplementedException(); }
}
public bool IsAuthenticated
{
get { throw new NotImplementedException(); }
}
public string Name
{
get { return _name; }
}
#endregion
}
Passing in these fake objects (along with your provided username of choice) into the Moq setup method for HttpContext is all there is to it! Now if your controller accesses this.User.Identity.Name in any of its methods, your unit tests such as the one below will work!
[Test]
public void Test_unit_test()
{
var mocks = new ContextMocks(controller);
mocks.SetupContextUser("10001");
var result = controller.Index(); //wont blow up!
//...
}
Setting the HttpMethod
This is pretty easy, and frankly, probably an unlikely thing to need. I stupidly implemented my solution thinking it was required to properly test my [AcceptVerbs(HttpVerbs.Post)] methods, when in fact it’s not! These methods all have a unique signature by default and running from a test system (and not MVC itself) they serve no function.
Still, your controllers may access Request.HttpMethod and in such a case, you’ll need to specify this in your mocked Request object. My solution was to make an overloaded constructor on the ContextMocks class, like so: (everything else in this method has been omitted for brevity)
public ContextMocks(Controller onController, string HTTPMethod)
{
//...
Request = new Moq.Mock<HttpRequestBase>();
Request.Setup(x => x.HttpMethod).Returns(HTTPMethod);
//...
}
Create a test like so, and you’re in business. Your controller can access Request.HttpMethod and it will have an appropriate value provided by your test!
[Test]
public void another_test_test()
{
var mocks = new ContextMocks(controller, "POST");
var result = controller.Index();
Assert.IsNotNull(result);
}
Hope this helps!
Tags: moq, user.identity.name, httpmethod, contextmocks
Categories: ASP.NET MVC, Unit Testing