Advanced CI: integrating web applications


So you’ve already got an automated build with unit tests and deployment packaging.  But if you have a Web Application project as part of your solution, there is still the potential for some compilation errors to get past your builds.  So if your builds compile your solution, how can we run into compilation problems?  Let’s look at a simple scenario.

It happens every day

Suppose I’m creating a simple ASP.NET MVC application, and I put some code in the template (ASPX) page:

<h2>I HAZ A BUKKET</h2>

<% Html.ActionLink<HomeController>(action => action.Index(), "Home"); %>

Nothing fancy, I’m just creating a link to the home url.  But what happens when I mess something up here?

<h2>NOOO!!!! THEY BE STEALIN MY BUKKET</h2>

<% Html.ActionLink<HomeController>(action => action.IndexWHOOPS(), "Home"); %>

The “Index” method exists on the HomeController class, but the “IndexWHOOPS” method definitely does not.  This should result in a compilation error.  However, when I build the solution, I don’t have any errors:

========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

Succeeded, hooray!  My build is green, let’s deploy!  If I happen to navigate to the lolrus page above, I get an error:

Well that’s no good, my build says “green” but in reality I have compilation errors.  My confidence in my automated build process is shot now, as I can’t catch all compilation errors.  Someone could type in gibberish, and my build wouldn’t catch it.  There are ways to catch these problems, however.

Precompilation is the key

When ASP.NET 2.0 was released, an additional command-line tool, aspnet_compiler, was shipped with it to allow developers to pre-compile web pages, ensuring a snappy response.  Without precompilation, each page and user control’s ASPX and ASCX file are dynamically compiled when first requested, which caused a noticeable delay on the client side.

But I don’t want to wait for users to catch compilation errors in my templates, I want them caught during my build.  Since the tool to do so is already available via the command line, integrating it with NAnt or MSBuild is very straightforward.  In fact, there’s already an AspNetCompiler task built-in to MSBuild, so you’ll be able to use that.

Before we integrate precompilation into our NAnt build, our compile task looks like this:

<target name="compile">
    <exec program="${environment::get-folder-path('System')}..Microsoft.NETFrameworkv3.5msbuild.exe"
          commandline="mvcapp.sln" />
  </target>

We just need to add an additional compile step right after the solution compile that will perform our additional ASP.NET compilation:

<target name="compile">
    <exec program="${environment::get-folder-path('System')}..Microsoft.NETFrameworkv3.5msbuild.exe"
          commandline="mvctest.sln" />

    <exec program="${environment::get-folder-path('System')}..Microsoft.NETFrameworkv2.0.50727aspnet_compiler.exe"
        commandline="-v mvctest -p mvctestMvcApplication" />
  </target>

Right after the solution compile, I compile the web application.  Depending on your deployment scenarios, you may want to tweak the command-line options passed in.  The options I used simply compile the web application in-place, but if I wanted to I could supply an output directory to place the compiled files.

I’m just interested in receiving compiler errors, so what does our build tell us now?

[exec] c:devmvctestmvctestMvcApplicationViewsHomeIndex.aspx(8): erro
r CS1061: 'MvcApplication.Controllers.HomeController' does not contain a definit
ion for 'IndexWHOOPS' and no extension method 'IndexWHOOPS' accepting a first ar
gument of type 'MvcApplication.Controllers.HomeController' could be found (are y
ou missing a using directive or an assembly reference?)

BUILD FAILED

Now I get detailed errors when I have compilation errors in my ASPX, ASCX, and all other files that get dynamically compiled by ASP.NET.  I can again put code in the designer files with confidence that my backup partner (continuous integration) will catch problems that I miss.

Compile your designers

Designers provide a nice declarative syntax for UI elements.  Many .NET technologies include designers, including ASP.NET and WPF (XAML).  But if your designers aren’t compiled along with the rest of your build process, you have a hole in your build strategy as some bugs won’t be noticed until they’re seen by someone trying to view the UI.  This problem can be exacerbated if you have actual code in your designer files.

By compiling your designers in the rest of your CI process, you can again have the confidence that 100% of your application is free of compilation errors.  But since you already have automated UI tests, you caught the errors already, right?

Abusing using statements