Geeks With Blogs
// ThomasWeller C#/.NET software development, software integrity, life as a freelancer, and all the rest

While many of the single components of an ASP.NET MVC application are easy to test (-drive) in an isolated manner (e.g. controllers), this can be hard for some others (like e.g. model binders) - and sometimes it just doesn't make much sense to test a single piece of code in isolation (e.g. for security-related issues). In such a case, some sort of integration testing has to be done, which in the context of ASP.NET MVC typically implies the usage of a browser automation framework like e.g. Selenium RC, WatiN, or the Lightweight Test Automation Framework. Such tools automate a real browser instance and require the tested web site to be hosted on a web server. Consequently, this way of testing needs quite a few resources and introduces its own set of problems and error possibilities...

When looking for a way to make these kinds of tests a bit more developer-friendly and less resource-demanding and error-prone, I came across this blog post: Integration Testing Your ASP.NET MVC Application. It introduces a framework for integration testing ASP.NET MVC applications without the need for any browser or server, but still running in the real (non-mocked) ASP.NET runtime. - You may refer to the above post to learn more about the rationales, various possibilities and technical details of this so-called MvcIntegrationTestFramework (and to see some more examples of what you can do with it).

To integrate the framework more tightly with a Gallio/MbUnit context, I found it useful to write a base class for a custom test fixture, which basically is just a wrapper around the MvcIntegrationTestFramework. As a result, you are able to write tests like this against your MVC app (remember: without browser or server or whatever, it just works...):

[Test]

public void SimulateRequestToRootUrl()

{

    RunSession(session =>

    {

        // Request the root URL

        RequestResult result = session.ProcessRequest("~/");

 

        // assertions about the controller that was created to fulfill the request...

        var controller = result.ActionExecutedContext.Controller as HomeController;

        Assert.IsNotNull(controller);

 

        // assertions about the ActionResult...

        var viewResult = (ViewResult)result.ActionExecutedContext.Result;

        Assert.AreEqual("Index", viewResult.ViewName);

        Assert.AreEqual("Welcome to ASP.NET MVC!", viewResult.ViewData["Message"]);

 

        // assertions about the rendered HTML...

        Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));

    });

}

...or, testing a log-in/anti-forgery mechanism, like this:

[Test]

public void TryingToAccessSecuredPageWithoutLoggingInIsBeingRedirected()

{

    RunSession(session =>

    {

        RequestResult result = session.ProcessRequest(SecuredUrl);

 

        Assert.IsTrue(result.Response.IsRequestBeingRedirected);

    });

}

 

[Test]

public void CanAccessSecuredPageAfterLoggingIn()

{

    RunSession(session =>

    {

        // First redirect to log on page and get an anti forgery token               

        string loginRedirectUrl = session.ProcessRequest(SecuredUrl).Response.RedirectLocation;

        string loginPageResponseText = session.ProcessRequest(loginRedirectUrl).ResponseText;

        string antiForgeryToken = MvcUtils.ExtractAntiForgeryToken(loginPageResponseText);

 

        // Post the login form with the user credentials and the verification token

        var formData =  new NameValueCollection

        {

            { "username", "thomas" },

            { "password", "blah" },

            { "__RequestVerificationToken", antiForgeryToken }

        };

        session.ProcessRequest(loginRedirectUrl, HttpVerbs.Post, formData);

 

        // Now, after logging in, go to the secured page again...

        RequestResult result = session.ProcessRequest(SecuredUrl);

 

        // ...this time everything should be fine.

        Assert.AreEqual("Hello thomas!", result.ResponseText);

    });

}

The above code is self-explaining, I think. To make it work, your custom test fixture has to be derived from the MvcIntegrationFixture base class, which only needs the web app's base directory provided in its c'tor:

 

public class MvcIntegrationFixture : MvcIntegrationFixtureBase

{

    public MvcIntegrationFixture()

        : base(Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory +

                                "\\..\\..\\..\\MvcIntegrationFixtureDemo"))

    {

    }

 

    // Tests go here...

 

} // class MvcIntegrationFixture

Besides wrapping the MvcIntegrationTestFramework, the MvcIntegrationFixtureBase class also takes care of such things like copying the required binaries to the application's bin folder, or re-catching possible assertion exceptions across app-domain boundaries. Please refer to the xml-documentation in the sample code for details.

The sample solution

The sample code (VS 2008 solution) is available here. It contains the here shown (fully documented)  MvcIntegrationFixtureBase base class along with some more usage examples.

 

kickit
shoutit
delicious facebook digg reddit linkedin stumbleupon technorati mrwong yahoo google-48x48 twitter email favorites
Posted on Saturday, December 12, 2009 6:25 AM Unit Testing/TDD , Automation , ASP.NET (MVC) | Back to top


Comments on this post: Integration testing an ASP.NET MVC application without web server or browser

# re: Integration testing an ASP.NET MVC application without web server or browser
Requesting Gravatar...
I like this post, thanks
Left by activekita on Mar 24, 2010 9:04 AM

# re: Integration testing an ASP.NET MVC application without web server or browser
Requesting Gravatar...
Really cool and partially works for MVC3.0.

Some issues block it full functional is both
result.ActionExecutedContext and result.ResultExecutedContext are null when testing with MVC3.0 project.

Any idea to fix?

Thanks a lot,
Vince
Left by vshand on Sep 23, 2011 5:31 PM

Your comment:
 (will show your gravatar)
 


Copyright © Thomas Weller | Powered by: GeeksWithBlogs.net | Join free