Link here

Link here

Integration Testing Your ASP.NET MVC Application

ASP.NET, Agile, MVC, Testing 22 Comments »

Let’s start with a quick recap:

  • Unit tests test individual software components. They supply mocks or fake versions of dependencies (such as a database) so that the unit test doesn’t rely on any external code and any failures can be pinpointed exactly. Unit testing is central to Test Driven Development – the entire TDD process is driven by unit testing.
  • Integration tests test your entire software stack working together. These tests don’t mock or fake anything (they use the real database, and real network connections) and are good at spotting if your unit-tested components aren’t working together as you expected. In general, it’s best to put most of your effort into building a solid suite of unit tests, and then adding a few integration tests for each major feature so you can detect any catastrophic incompatibilities or configuration errors before your customers do.

ASP.NET MVC famously has great support for unit testing. That’s well covered elsewhere on the web, so I won’t write more about it here. But what about integration testing?

Typically, ASP.NET MVC developers have written integration tests using a browser automation tool, such as Selenium or Microsoft’s Lightweight Test Automation Framework. These tools host a real web browser and automate a series of navigation steps, clicking on buttons and letting you write assertions about what should appear on the screen. Browser automation is fully capable of testing your JavaScript code just as easily as your business logic. However, there are also some drawbacks to browser automation:

  • Browser automation test scripts are very fragile. As soon as you alter the HTML in your views, the tests may start failing and sometimes all need to be re-recorded. Many developers are happy with browser automation at first, but after a few months are sick of re-recording the tests or maintaining thousands of lines of scripts, and give up on it.
  • Browser automation test scripts rely on parsing the generated HTML, which loses all the strongly-typed goodness of your MVC model objects.

So, how can we do integration testing without browser automation?

Introducing MvcIntegrationTestFramework

What if you could write NUnit tests (or xUnit or whatever you prefer) that directly submit a URL, query string, cookies, request headers, etc., into your application – without needing the app to be hosted in any web server but still running in the real (non-mocked) ASP.NET runtime – and get back the response text to make assertions about? In fact, what if you also got back the ActionResult (e.g., a ViewResult) that was executed, a reference to any unhandled exception it threw, and could also write assertions about cookie values or the contents of Session? What if you could string together a series of test requests to simulate a user’s browsing session, checking that a whole feature area works from end to end?

Well, my friend, all this can be yours with MvcIntegrationTestFramework :)

Download the demo project. To run the integration tests, you’ll need to install NUnit or some other test runner.

A Simple Integration Test Example

Consider the following action method:

public ActionResult Index()
{
    ViewData["Message"] = "Welcome to ASP.NET MVC!";
    return View();
}

You can write an integration for this using MvcIntegrationTestFramework as follows:

[Test]
public void Root_Url_Renders_Index_View()
{
    appHost.SimulateBrowsingSession(browsingSession => {
        // Request the root URL
        RequestResult result = browsingSession.ProcessRequest("/");
 
        // You can make 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"]);
 
        // ... or you can make assertions about the rendered HTML
        Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
    });
}

This is pretty similar to a typical unit test for that action method, except that you also get access to the finished rendered HTML, in case that interests you. Unlike a unit test, this integration test goes through the entire request-processing pipeline, so it tests your routing configuration, your controller factory, any dependencies your controller has, and even your view template.

Note: That doesn’t make it better than a unit test: it just solves a different problem. Unit tests are better for driving the TDD process and for proving the correctness of individual components in isolation, and they tend to be easier to maintain large volumes of over time. Integration tests, on the other hand, are good in small doses: they cover things that unit tests cannot (such as views, database connectivity, etc.) but aren’t so fine-grained as unit tests.

Integration Tests Cover End-to-end Scenarios

Consider the following sequence of behaviours:

1. When a logged-out user visits /Home/SecretAction, they should be redirected to the login screen

2. If the user then enters a valid username and password, they should be redirected back to /Home/SecretAction

3. Since the user is now logged in, they should be shown some secret information

That’s a lot of interaction between action methods, Forms Authentication, HTTP redirections, cookies, and filters. You wouldn’t typically cover that interaction in any unit test, because each of those components is a separate unit. However, if you have an end-to-end unit test that runs through the whole process, you can feel very confident that you haven’t unintentionally broken anything (e.g., by editing web.config).

Here’s a example end-to-end integration test for this whole sequence of behaviours:

[Test]
public void LogInProcess()
{
    string securedActionUrl = "/home/SecretAction";
 
    appHost.SimulateBrowsingSession(browsingSession => {
        // First try to request a secured page without being logged in                
        RequestResult initialRequestResult = browsingSession.ProcessRequest(securedActionUrl);
        string loginRedirectUrl = initialRequestResult.Response.RedirectLocation;
        Assert.IsTrue(loginRedirectUrl.StartsWith("/Account/LogOn"), "Didn't redirect to logon page");
 
        // Now follow redirection to logon page
        string loginFormResponseText = browsingSession.ProcessRequest(loginRedirectUrl).ResponseText;
        string suppliedAntiForgeryToken = MvcUtils.ExtractAntiForgeryToken(loginFormResponseText);
 
        // Now post the login form, including the verification token
        RequestResult loginResult = browsingSession.ProcessRequest(loginRedirectUrl, HttpVerbs.Post, new NameValueCollection
        {
            { "username", "steve" },
            { "password", "secret" },
            { "__RequestVerificationToken", suppliedAntiForgeryToken }
        });
        string afterLoginRedirectUrl = loginResult.Response.RedirectLocation;
        Assert.AreEqual(securedActionUrl, afterLoginRedirectUrl, "Didn't redirect back to SecretAction");
 
        // Check that we can now follow the redirection back to the protected action, and are let in
        RequestResult afterLoginResult = browsingSession.ProcessRequest(securedActionUrl);
        Assert.AreEqual("Hello, you're logged in as steve", afterLoginResult.ResponseText);
    });
}

In this test, multiple requests are made as part of the same browsing session. The integration testing framework will preserve cookies (and therefore session state) from one request to the next, so you can test interaction with things like Forms Authentication and ASP.NET MVC’s AntiForgeryToken helpers. You can see this whole thing working by downloading the demo project.

This has advantages over browser automation in that (in my opinion) it’s easier to write this test – it’s concise, you don’t need to learn a browser scripting language, and you don’t need to rerecord the script if you restructure the HTML. You can make assertions at the HTML level, or you can make assertions at the ActionResult level, with fully strongly-typed access to any ViewData or Model values that were being supplied.

On the flip side, browser automation is still the only way to test your JavaScript.

Integration Testing Your Own ASP.NET MVC Application

To add these kinds of tests to your own ASP.NET MVC application, create a new class library project called MyApp.IntegrationTests or similar. Add a reference to MvcIntegrationTestFramework.dll, which you can get by downloading the demo project and compiling it, and also add a reference to your chosen unit testing framework (e.g., NUnit or xUnit).

Next, if you’re using NUnit, create a basic test fixture class as follows:

using MvcIntegrationTestFramework.Hosting;
using MvcIntegrationTestFramework.Browsing;
 
[TestFixture]
public class MyIntegrationTests
{
    // Amend this path to point to whatever folder contains your ASP.NET MVC application
    // (i.e., the folder that contains your app's main web.config file)
    private static readonly string mvcAppPath = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + "\\..\\..\\..\\MyMvcApplication");
    private readonly AppHost appHost = new AppHost(mvcAppPath);
}

(If you’re using something other than NUnit, amend the syntax accordingly.)

Also – and seriously you can’t skip this step – you must add a post-build step so that each time you compile, Visual Studio will copy the assemblies from your integration test project’s \bin folder into your main ASP.NET MVC application’s \bin folder. I’ll explain why this is necessary later.

image

The test fixture you just created tells MvcIntegrationTestFramework to find your ASP.NET MVC application at the specified disk location, and to host it (without using IIS or any other web server). The resulting AppHost object then provides an API for issuing requests to your application, so you could write a test as follows:

[TestFixture]
public class MyIntegrationTests
{
    // Leave rest as before
 
    [Test]
    public void HomeIndex_DemoTests()
    {
        appHost.SimulateBrowsingSession(browsingSession => {
            RequestResult result = browsingSession.ProcessRequest("home/index");
 
            // Routing config should match "home" controller
            Assert.AreEqual("home", result.ActionExecutedContext.RouteData.Values["controller"]);
 
            // Should have rendered the "index" view
            ActionResult actionResult = result.ActionExecutedContext.Result;
            Assert.IsInstanceOf(typeof(ViewResult), actionResult);
            Assert.AreEqual("index", ((ViewResult)actionResult).ViewName);
 
            // App should not have had an unhandled exception
            Assert.IsNull(result.ResultExecutedContext.Exception);
        });
    }
}

The browsingSession object lets you read and write cookies and ASP.NET session values (note: ASP.NET initializes the Session collection only after the first request in any given browsing session). The RequestResult object that comes back from ProcessRequest() lets you access the rendered response text, the actual HttpResponse object, plus the MVC ActionExecutedContext and RequestExecutedContext objects that specify what the application did in ASP.NET MVC terms.

How Does it Work Internally?

Note: if you just want to use MvcIntegrationTestFramework, you don’t need to know any of the following. I provide this info just for your interest, and to give you a head start if you want to expand the framework or change it in any way.

MvcIntegrationTestFramework uses .NET’s built-in API for creating an ASP.NET-enabled .NET appdomain and using it to host an ASP.NET application from an arbitrary disk location. The test framework then does a bunch of funky reflection-assisted stuff to attach some interceptors and event handlers to the new HTTP runtime.

When you call ProcessRequest(), internally it calls ASP.NET’s HttpRuntime.ProcessRequest() method, which invokes the request-processing pipeline. The testing framework simulates an incoming HttpWorkerRequest according to the URL, HTTP method, querystring, form values, cookies, and headers that you specify.

While the request is running, MvcIntegrationTestFramework uses its knowledge of ASP.NET MVC to add interceptors to key points in the MVC request-processing pipeline. This is how it’s able to grab and expose the ActionResult and other MVC context objects so you can make assertions about them.

Of course, all this request processing has to happen within the ASP.NET-enabled appdomain, *not* the appdomain your test runner invokes the unit test on. So, we use the slightly bizarre appHost.SimulateBrowsingSession(browsingSession => { /* your code goes here */ } syntax to shuttle your test code across the appdomain boundary and run it there. Consequently, any variables you declare outside your anonymous delegate and then use inside it *must* be serializable, or you’ll get a serialization error when they get marshalled across the appdomain boundary. This is unlikely to be a problem in real world use.

Since your test code runs in the ASP.NET-enabled appdomain, the CLR’s assembly loader will need to load your test code assembly from a location accessible to your ASP.NET MVC application. That’s why it’s necessary to have a post-build event to copy your test code assembly (and any other assemblies it references) into your ASP.NET MVC app’s \bin folder. Or you could register your test assembly in the GAC I suppose, but that’s much more hassle.

Conclusion

Personally, I find this approach to integration testing more productive than browser automation in most cases, because the tests are fast to write and are less prone to break because of trivial HTML changes. I wouldn’t want to have thousands of integration tests, though – for one thing, they’d take ages to run – but end-to-end testing for key scenarios like logging in, registering, going through shopping carts, etc., proves pretty convincingly that the major functional areas of your app are working overall, leaving unit tests to prove the fine detail of each component.

Some of the code in MvcIntegrationTestFramework was adapted from Chot’s CodeProject article, some of the ideas came from a discussion at DDD South West with Richard Fennell who’s already doing something similar except with a dependency on TypeMock, and some bits of my HttpWorkerRequest simulation code were demonstrated years ago by Phil Haack in his HttpSimulator code, which is similar to MvcIntegrationTestFramework except it predates ASP.NET MVC.

Download the demo project. To run the integration tests, you’ll need to install NUnit or some other test runner.

kick it on DotNetKicks.com

Now Published: Pro ASP.NET MVC Framework (Apress)

MVC, Pro ASP.NET MVC 49 Comments »

image At last! Physical copies of my book have finally reached the public. I’ve been writing, tweaking, fixing, expanding, and polishing this since February 2008, following every preview release of ASP.NET MVC. Now it all reflects the final RTM version.

The design goal for this book is to give you, the reader, the most practical and detailed guide to virtually everything that ASP.NET MVC does. If you want to become your company’s expert on how this stuff really works – how to bend it to your will, how to apply best practises, and what limitations you’ll have to overcome – then I hope this is the book for you.

Download a sample chapter - an introductory tutorial that shows how to build a simple data-entry application with ASP.NET MVC.

Of course, other MVC books will become available over the next few months. I’ll look forward to reader feedback about how these compare.

To give you a feel for what’s covered, here’s the table of contents:

Part 1: Introducing ASP.NET MVC

CHAPTER 1 : What’s the Big Idea?

A Brief History of Web Development
Traditional ASP.NET
What’s Wrong with Traditional ASP.NET?
Web Development Today
Web Standards and REST.
Agile and Test-Driven Development
Ruby on Rails
Key Benefits of ASP.NET MVC
Model-View-Controller Architecture
Extensibility
Testability
Tight Control over HTML
Powerful New Routing System
Built on the Best Parts of the ASP.NET Platform
.NET 3.5 Language Innovations
ASP.NET MVC Is Open Source
Who Should Use ASP.NET MVC?
Comparisons with ASP.NET WebForms
Comparisons with Ruby on Rails
Comparisons with MonoRail

CHAPTER 2 : Your First ASP.NET MVC Application

Preparing Your Workstation
Creating a New ASP.NET MVC Project
Removing Unnecessary Files
How Does It Work?
Rendering Web Pages
Creating and Rendering a View
Adding Dynamic Output
A Starter Application
The Story
Linking Between Actions
Designing a Data Model
Building a Form
Handling Form Submissions
Adding Validation
Finishing Off

CHAPTER 3 : Prerequisites

Understanding Model-View-Controller Architecture
The Smart UI (Anti-Pattern)
Separating Out the Domain Model
Three-Tier Architecture
Model-View-Controller Architecture
Variations on Model-View-Controller
Domain Modeling
An Example Domain Model
Entities and Value Objects
Ubiquitous Language
Aggregates and Simplification
Keeping Data Access Code in Repositories
Using LINQ to SQL
Building Loosely Coupled Components
Taking a Balanced Approach
Using Inversion of Control
Using an IoC Container
Getting Started with Automated Testing
Unit Tests and Integration Tests
The Red-Green Development Style
New C# 3 Language Features
The Design Goal: Language Integrated Query
ExtensionMethods
Lambda Methods
GenericType Inference
Automatic Properties
Objectand Collection Initializers
Type Inference
Anonymous Types
Using LINQ to Objects
Lambda Expressions
IQueryable<T> and LINQ to SQL

CHAPTER 4 : SportsStore: A RealApplication

Getting Started
Creating Your Solutions and Projects
Starting Your Domain Model
Creating an Abstract Repository
Making a Fake Repository
Displaying a List of Products
Removing Unnecessary Files
Adding the First Controller
Setting Up the Default Route
Adding the First View
Connecting to a Database
Defining the Database Schema
Setting Up LINQ to SQL
Creating a Real Repository
Setting Up Inversion of Control
Creating a Custom Controller Factory
Using Your IoC Container
Creating Automated Tests
Configuring a Custom URL Schema
Adding a RouteTable Entry
Displaying Page Links
Styling It Up
Defining Page Layout in the Master Page
Adding CSS Rules
Creating a Partial View

CHAPTER 5 : SportsStore: Navigation andShopping Cart

Adding Navigation Controls
Filtering the Product List
Defining a URL Schema for Categories
Building a Category Navigation Menu
Building the Shopping Cart
Defining the Cart Entity
Adding “Add to Cart” Buttons
Giving Each Visitor a Separate Shopping Cart
Creating CartController
Displaying the Cart
Removing Items from the Cart
Displaying a Cart Summary in the Title Bar
Submitting Orders
Enhancing the Domain Model
Adding the “Check Out Now” Button
Prompting the Customer for Shipping Details
Defining an Order Submitter IoC Component
Completing CartController
Implementing the EmailOrderSubmitter

CHAPTER 6 : SportsStore: Administrationand Final Enhancements

Adding Catalog Management
Creating AdminController: A Place for the CRUD Features
Rendering a Grid of Products in the Repository
Building a Product Editor
Creating New Products
Deleting Products
Securing the Administration Features
Setting Up Forms Authentication
Using a Filter to Enforce Authentication
Displaying a Login Prompt
Image Uploads
Preparing the Domain Model and Database
Accepting File Uploads
Displaying Product Images

Part 2: ASP.NET MVC in Detail

CHAPTER 7 : Overview of ASP.NET MVC Projects

Developing MVC Applications in Visual Studio
The Default MVC Project Structure
Naming Conventions
The Initial Application Skeleton
Debugging MVC Applications and Unit Tests
Using the Debugger
Stepping into the .NET Framework Source Code
Stepping into the ASP.NET MVC Source Code
The Request Processing Pipeline
Stage 1: IIS
Stage 2: Core Routing
Stage 3: Controllers and Actions
Stage 4: Action Results and Views

CHAPTER 8 : URLs and Routing

Putting the Programmer Back in Control
Setting Up Routes
Understanding the Routing Mechanism
Adding a Route Entry
Using Parameters
Using Defaults
Using Constraints
Accepting a Variable-Length List of Parameters
Matching Files on the Server’s Hard Disk
Using IgnoreRoute to Bypass the Routing System
Generating Outgoing URLs
Generating Hyperlinks with Html.ActionLink
Generating Links and URLs from Pure Routing Data
Performing Redirections to Generated URLs
Understanding the Outbound URL-Matching Algorithm
Generating Hyperlinks with Html.ActionLink<T> and Lambda Expressions
Working with Named Routes
Unit Testing Your Routes
Testing Inbound URL Routing
Testing Outbound URL Generation
Further Customization
Implementing a Custom RouteBase Entry
Implementing a Custom Route Handler
URL Schema Best Practices
Make Your URLs Clean and Human-Friendly
Follow HTTP Conventions
Search Engine Optimization

CHAPTER 9 : Controllers and Actions

An Overview
Comparisons with ASP.NET WebForms
All Controllers Implement IController
The Controller Base Class
Receiving Input
Getting Data from Context Objects
Using Action Method Parameters
Invoking Model Binding Manually in an Action Method
ProducingOutput
Understanding the ActionResult Concept
Returning HTML by Rendering a View
Performing Redirections
Returning Textual Data
Returning JSON Data
Returning JavaScript Commands
Returning Files and Binary Data
Creating a Custom Action Result Type
Using Filters to Attach Reusable Behaviors
Introducing the Four Basic Types of Filters
Applying Filters to Controllers and Action Methods
Creating Action Filters and Result Filters
Creating and Using Authorization Filters
Creating and Using Exception Filters
Bubbling Exceptions Through Action and Result Filters
The [OutputCache] Action Filter
Other Built-In Filter Types
Controllers As Part of the Request Processing Pipeline
Working with DefaultControllerFactory
Creating a Custom Controller Factory
Customizing How Action Methods Are Selected and Invoked
Testing Controllers and Actions
How to Arrange, Act, and Assert
Testing a Choice of View and ViewData
Testing Redirections
More Comments About Testing
Mocking Context Objects

CHAPTER 10 : Views

How Views Fit into ASP.NET MVC
The WebForms View Engine
View Engines Are Replaceable
WebForms View Engine Basics
Adding Content to a View Template
Five Ways to Add Dynamic Content to a View Template
Using Inline Code
Why Inline Code Is a Good Thing in MVC View Templates
Understanding How MVC Views Actually Work
Understanding How ASPX Templates Are Compiled
Understanding ViewData
Rendering ViewData Items Using ViewData.Eval
Using HTML Helper Methods
The Framework’s Built-In Helper Methods
Creating Your Own HTML Helper Methods
Using Partial Views
Creating a Partial View
Rendering a Partial View Using Server Tags
Using Html.RenderAction to Create Reusable Widgets with
Application Logic
What Html.RenderAction Does
When It’s Appropriate to Use Html.RenderAction
Creating a Widget Based on Html.RenderAction
Sharing Page Layouts Using Master Pages
Using Widgets in MVC View Master Pages
Implementing a Custom View Engine
A View Engine That Renders XML Using XSLT
Using Alternative View Engines
Using the NVelocity View Engine
Using the Brail View Engine
Using the Spark View Engine
Using the NHaml View Engine

CHAPTER 11 : Data Entry

Model Binding
Model-Binding to Action Method Parameters
Model-Binding to Custom Types
Invoking Model Binding Directly
Model-Binding to Arrays, Collections, and Dictionaries
Creating a Custom Model Binder
Using Model Binding to Receive File Uploads
Validation
Registering Errors in ModelState
View Helpers for Displaying Error Information
How the Framework Maintains State in Input Controls
Performing Validation During Model Binding
Moving Validation Logic into Your Model Layer
About Client-Side (JavaScript) Validation
Wizards and Multistep Forms
Verification
Implementing a CAPTCHA
Confirmation Links and Tamper-Proofing with HMAC Codes

CHAPTER 12 : Ajax and Client Scripting

Why You Should Use a JavaScript Toolkit
ASP.NET MVC’s Ajax Helpers
Fetching Page Content Asynchronously Using Ajax.ActionLink
Submitting Forms Asynchronously Using Ajax.BeginForm
Invoking JavaScript Commands from an Action Method
Reviewing ASP.NET MVC’s Ajax Helpers
Using jQuery with ASP.NET MVC
Referencing jQuery
Basic jQuery Theory
Adding Client-Side Interactivity to an MVC View
Ajax-Enabling Links and Forms
Client/Server Data Transfer with JSON
Fetching XML Data Using jQuery
Animations and Other Graphical Effects
jQuery UI’s Prebuilt User Interface Widgets
Implementing Client-Side Validation with jQuery
Summarizing jQuery

CHAPTER 13 : Security and Vulnerability

All Input Can Be Forged
Forging HTTP Requests
Cross-Site Scripting and HTML Injection
Example XSS Vulnerability
ASP.NET’s Request Validation Feature
Filtering HTML Using the HTML Agility Pack
Session Hijacking
Defense via Client IP Address Checks
Defense by Setting the HttpOnly Flag on Cookies
Cross-Site Request Forgery
Attack
Defense
Preventing CSRF Using the Anti-Forgery Helpers
SQL Injection
Attack
Defense by Encoding Inputs
Defense Using Parameterized Queries
Defense Using Object-Relational Mapping
Using the MVC Framework Securely
Don’t Expose Action Methods Accidentally
Don’t Allow Model Binding to Change Sensitive Properties

CHAPTER 14 : Deployment

Server Requirements
Requirements for Shared Hosting
IIS Basics
Understanding Web Sites and Virtual Directories
Binding Web Sites to Hostnames, IP Addresses, and Ports
How IIS Handles Requests and Invokes ASP.NET
Deploying Your Application
Copying Your Application Files to the Server
Using Visual Studio ’s Publish Feature
Making It Work on Windows Server /IIS 6
Making It Work on IIS 7
Making Your Application Behave Well in Production
Supporting Changeable Routing Configurations
Supporting Virtual Directories
Using ASP.NET’s Configuration Facilities
Controlling Compilation on the Server
Detecting Compiler Errors in Views Before Deployment

CHAPTER 15 : ASP.NET Platform Features

Windows Authentication
Preventing or Limiting Anonymous Access
Forms Authentication
Setting Up Forms Authentication
Using Cookieless Forms Authentication
Membership, Roles, and Profiles
Setting Up a Membership Provider
Using a Membership Provider with Forms Authentication
Creating a Custom Membership Provider
Setting Up and Using Roles
Setting Up and Using Profiles
URL-Based Authorization
Data Caching
Reading and Writing Cache Data
Using Advanced Cache Features
Site Maps
Setting Up and Using Site Maps
Creating a Custom Navigation Control with the Site Maps API
Generating Site Map URLs from Routing Data
Internationalization
Setting Up Internationalization
Tips for Working with Resource Files
Using Placeholders in Resource Strings
Performance
HTTP Compression
Tracing and Monitoring
Monitoring Page Generation Times
Monitoring LINQ to SQL Database Queries

CHAPTER 16 : Combining MVC and WebForms

Using WebForms Technologies in an MVC Application
Using WebForms Controls in MVC Views
Using WebForms Pages in an MVC Web Application
Adding Routing Support for WebForms Pages
Using ASP.NET MVC in a WebForms Application
Upgrading an ASP.NET WebForms Application to Support MVC
Getting Visual Studio to Offer MVC Items
Interactions Between WebForms Pages and MVC Controllers
Site Meter