views:

40

answers:

2

So I'm trying to unit-test a controller method. I'm using MSTest in VS 2010, and Moq 3.1

Test method:

    [TestMethod]
    public void TestAccountSignup()
    {
        var request = new Mock<HttpRequestBase>();
        var context = new Mock<HttpContextBase>();

        AccountController controller = new AccountController();
        controller.ControllerContext = new System.Web.Mvc.ControllerContext(context.Object, new RouteData(), controller);

        request.Setup(x => x.Cookies).Returns(new HttpCookieCollection());

        context.Setup(x => x.Request).Returns(request.Object);

        string username = StringHelper.GenerateRandomAlpha(10);

        var res = controller.Register(username, "foozbaaa+" + username + "@example.com", null, true, "Testing!", null);
    }

My controller method:

   [CaptchaValidator]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Register(string userName, string email,string existingUsername, bool captchaValid, string heardAbout, string heardAboutOther)
    {
      //Loads of stuff here....

        //cool - all registered
       //This line gives the problem
     return new RedirectResult(this.BuildUrlFromExpression<AccountController>(x => x.AccountCreated()));
     }

The controller method works just fine when not unit testing.

When mocking and calling in this way, I get a System.Security.VerificationException on that last line:

Method Microsoft.Web.Mvc.LinkBuilder.BuildUrlFromExpression: type argument 'TController' violates the constraint of type parameter 'TController'.

Now clearly AccountController is of type TController, otherwise it wouldn't work when not unit-testing. It inherits from my BaseController, which inherits from the regular Controller.

I get the feeling this error is a red-herring, due to the mocking - any ideas why?

Many thanks.

+1  A: 

This is because Futures uses a slightly different Controller type.

I'm not sure of the specifics but I encountered the same issue. Its either a futures Controller vs a MVC 2 Controller or MVC 1 Controller vs a MVC 2 controller.

See if fully qualifying the type name helps.

jfar
It ended up being exactly that - I had a futures v1 dll referenced in my test project, but v2 in the actual app. Many thanks.
Nik
A: 

How about the following:

[HttpPost]
public ActionResult Register()
{
    return this.RedirectToAction<AccountController>(x => x.AccountCreated());
}

RedirectToAction<T> is an extension method to the Controller class defined in Microsoft.Web.Mvc.ControllerExtensions (part of ASP.NET MVC futures).

Now it's much easier to unit test. You don't even need to mock the context:

// arrange
var controller = new AccountController();

// act
var actual = controller.Register();

And if you use the excellent MvcContrib.TestHelper which I would strongly recommend you, the assert part of your unit test could look like this:

// assert
actual
    .AssertActionRedirect()
    .ToAction<AccountController>(x => x.AccountCreated());
Darin Dimitrov
Thanks a stack for the heads up on the Contrib tester stuff and the extension method - that's excellent.
Nik