Click here to Skip to main content
Click here to Skip to main content

Integration testing an ASP.NET MVC application without web server or browser

, 4 Aug 2010
Rate this:
Please Sign up or sign in to vote.
Integration testing an ASP.NET MVC application without web server or browser

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 (fully documented) MvcIntegrationFixtureBase base class shown here along with some more usage examples.

<iframe marginwidth="0" marginheight="0" src=""http://ads.geekswithblogs.net/a.aspx?ZoneID=5&Task=Get&PageID=31016&SiteID=1"" frameborder="0" width="1" scrolling="no" height="1" />

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Thomas Weller
Software Developer (Senior) Freelancer
Germany Germany
I'm a freelance software developer/architect located in Southern Germany with 10+ years of experience in C++, VB, and C# software projects. In the last few years I do all my coding exclusively in C#.
I am especially dedicated to Test-driven development, OO architecture, and software quality assurance issues and tools.
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 4 Aug 2010
Article Copyright 2010 by Thomas Weller
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid