Extreme Web Architectures - Testing Web sites in Seconds

Abstract

In the agile process Extreme Programming(XP) a key practice is automating testing. It is part of Test Driven Development (TDD), Refactoring and Continuous Integration. In XP it is important that systems are highly testable and that the tests run fast. This article discusses the concept of Extreme Web Architectures that are designed to maximise testability and also ensure that a full regression test can be conducted in under 1 minute. It also describes a real life architecture built with those goals in mind. The architecture enables the developer to test very quickly all HTML pages without having to load the pages into a browser.  

Motivation - Benefits of Quality Assurance

It is claimed that the Quality Assurance(QA) activities in XP flatten the cost curve of a project in terms of the cost of defects over time. If we look at the traditional cost curve of finding defects then we see that the cost of defects rises exponentially over time. A defect could range from a low level bug to something more costly like getting the requirements wrong.

The practices in XP attack this cost risk from requirements, through to development and testing. XP iterations can range from 10 minutes through to a month. The key is that there is lots of QA activity going on all the time. So, instead of one long exponential curve, you get lots of smaller exponential curves due to the iterations being so small. With the small iterations and lots of feedback, combined with the continuous testing and integration you are effectively resetting the cost curve more often before it gets out of control. Hence, the flatter curve when you compare it to the more traditional one.

Keeping The Tests Running Fast

Tests must be quick to ensure they are run continuously. If they are slow then the continuous integration process becomes less frequent and quality suffers as developers fight to get the application running again. With slow tests the refactoring process becomes more time consuming, and it tends not to occur as much, which results in increased code rot. Once quality suffers, productivity slows down.

In a nutshell, you have got to get those tests running as fast as you can.

What is an Extreme Web Architecture?

An Extreme Web Architecture is one designed to be fully testable within seconds, which can then be used to build web applications which can then be tested in under one minute. The full set of proposed qualifications are below:

Qualifications For An Extreme Web Architecture

  • Built 100% test first
  • Exhaustive set of unit tests
  • Massive decoupling, resulting in the ability to apply a unit test at any point within the architecture.
  • Ability to substitute any component in the architecture with a mock object.
  • The whole architecture can be fully regression tested in under 5 seconds.
  • Any web application built using the architecture can be tested in under one minute.

Extreme Web Architecture Example – using .NET

In this section I describe an Extreme Web Architecture that was built using Quad Programming . The Design Patterns quoted can be found in Design Patterns[Gamma et al.] and Patterns of Enterprise Application Architecture [Fowler].

Designing For Fast Tests

In order to get the tests running fast we need to attack a number of areas where testing is commonly slow. By using mock objects we can mock out areas of the application that are typically slow.

In a web application the slowness of the front end makes is very difficult to automate fast testing. To get the tests quick we must avoid the need to fire up a browser to run the tests. Granted, automating test using IE is quicker than manual testing, but to pass the Extreme Web Application requirements it won’t be fast enough.

Although not web specific, another area to apply mock objects is at the persistence layer. Connecting to a persistence storage (e.g. SQL Server) is slow when done repeatedly in automated tests. Solutions to these problems will be discussed in detail. First, an overview of the architecture.

Architectural Overview

The architecture is built using some of the following patterns: Model View Controller, Two-Phase View, Application Controller, Intercepting Filter, Chain of Responsibility, Logical View, Composite, Singleton, Strategy, Factory Method, Page Controller, and Builder.

The application is written in C#, using ASP.NET for session persistence, and XML/XSL for the HTML rendering mechanism.

To describe the way it hangs together lets first look at the filter chain (Intercepting Filter) that sits in front of the Controllers and Views in the Application Controller.

The Filter Chain

The filter chain consists of the following filters:
  1. Get Session Filter: This retrieves the users strongly typed session from the httpSession so it is available for the length of the request.
  2. View Identification Filter: This determines the logical view that originated the event from the front end.
  3. Synchronisation Filter: Takes the httpRequest and synchronises the logical views held in the user session with values from the httpContext that get submitted as part of the request.
  4. Event Filter: This identifies the event that the user requested and invokes the event on the relevant controller. The controller then builds a new logical view.
  5. Render Filter: This takes the view built by the controller, serialises it to XML, transforms it to HTML using XSL, then writes the HTML to the httpResponse.
  6. Save Session Filter: This saves the users strongly typed session into the httpSession so it is available for retrieving on the users next request.
  7. Response End Filter: This simply ends the httpResponse and returns control to the browser.

There are some potential filters not included. An Authentication Filter could check to see if the user is allowed to use the system, and a Permissions Filter could check to see if the user has permission to execute a particular request (Event).

Events are declared as delegate methods using .NET Attributes on the Controllers. At start up an Event Manager uses Reflection to register all the events. An event delegate takes an Abstract View as its argument.

Getting Data To The Screen

An event gets raised (either application start, or a user request). Controllers have the responsibility of reacting to events. They build strongly typed Logical Views (C# objects) of the response and park their result into the CurrentView property of the Session. Logical Views can be combined as per the Composite pattern.

The Render Filter takes the current Logical View and then uses a series of Builders to turn the Logical View into XML. Some of the properties of the Views do not need to be put into the XML which the Builders recognise by the use of a BuilderSuppress Attribute.

After the XML is built the Render Filter transforms the XML into HTML using XSL for that view. The HTML is then written to the httpResponse.

Any information written to the users strongly typed Session is persisted to the users httpSession at the end of the request by the Save Session Filter.

Finally, the Response End Filter is called to end the httpResponse.

Reacting to a User Event

When the httpRequest is sent in from the front end, the Application Controller intercepts the request and invokes the first filter in the chain, namely the Get Session Filter. The Get Session Filter retrieves the users strongly typed session from the httpSession ready for use during the request.

Next, the Synchronization Filter synchronises the values from the request with the Logical Views that were previously rendered and saved in the CurrentView property of the users Session. The synchronisation uses the Builder pattern and reflection to take values from the HttpRequest.RequestParms collection and populates the views with any changes. Like the XML Builder process there are some values that do not need to be synchronised. These are identified by the synchronisers IgnoreOnSynchronisation Attribute.

After synchronisation the Event Filter gets the name of the event that was invoked from the browser and pulls the delegate from the Event Registry. The delegate is then invoked which results in the correct method being called on the appropriate controller.

The controller then deals with the request, and builds a new Logical View and we are back to where we started with getting data to the client.

Testability Advantages

We can now fully automate the testing of the system without having to fire up browser. We can build views, call the Controller, and check that the correct response was returned. [This technique is nothing particularly new. On many other articles you will see developers using the common Model View Controller pattern with a Seperated Interface for the Logical View and the subsequent use of a Mock Object for the testing of the controller. It is a standard and very effective technique.]

We can test the XSL by creating a Logical View, calling the Render Filter, and then asserting that the HTML created has the correct elements with the correct values. If they have the correct values we know that when we synchronise a request the Views will be populated correctly. Thus, no need to fire up the browser. [This part of the architecture is not such a common technique, but as we will see later it is a very powerful one.]

Other Advantages

We have complete separation of the presentational aspects using XSL. If we want to we can enhance the render filter to choose a different XSL page to render the same logical view in a different format. Suffice to say, it is very easy to make changes to the front end. It is also much quicker and easier to convert HTML provided by a designer into XSL, than it is to convert it to an ASP.NET Web Form.

Because of the separation of concerns we can also build parts of the system independently. We can stub out (Service Stub) or use Mock Objects on any area of the system.

We now have strong typing since the views and synchronisers are responsible for type validation. By the time the response hits the Controller all type checks have been done.

Building and Synchronisation Of Logical Views

This part is key to the testability of the system. If we can trust the building and synchronisation then we have cracked the need to fire up a browser whilst still being able to actually test that the HTML works.

The render filter uses a series of XML builders to create the XML using Reflection. The builders iterate through the views and then build a single XML string of the views, and the embedded views. Each view has a unique id. When the HTML is rendered via XSL each HTML element is also given a unique id.

During synchronisation, the synchronisers use reflection to iterate through the views again. The values of the view are updated based on any changes in the HTML elements as recognised by the previous id's.

This is all done using reflection and attribute programming.

Mocking out the HttpContext

The application uses the httpContext to persist Application and Session data. In order to get the tests running in NUnit we need to remove the coupling to the httpContext. For example, to test the synchronisers we needed to simulate them receiving a Named Value Collection in the same way it would from an httpContext object.

To do this we created a class called the AbstractContext with the same interface as the htttpContext. We then created two implementations of AbstractContext: one that wrapped the real httpContext, and the other that simulated it using standard .NET collections. When we used the real application the Application Controller then set the AbstractContext.Current = WebContext(), and during testing we set the AbstractContext.Current = TestContext(). Then all the code referred to the AbstractContext. We could then test the application without being bound to Http. This we 'mocked' out the httpContext. 

Practicalities of Testing The Front End

To test the front end we take the html response from the Render Filter. This is then used to create an XHtmlDocument. The constructor takes the original html, makes it well formed, and then forms a wrapper around a normal .NET XmlDocument. We can then query the document for certain types of Html element to see if the rendering worked correctly.

Mocking out the Persistence Layers

Below the Controller level there is a Command layer, a Domain Model and a whole bunch of layers that do the mapping of the Domain to persist the data in relational form.

To keep the tests quick we need to mock out the need to talk to the database. To do this we use mock objects. When domain objects make use of Mappers we simply substitute for a Mock Mapper that returns hard coded data. This removes the time consuming database calls and also allows us to subsitute different Mappers that behave differently. For example, we can use a Mock Mapper than raises an exception indicating the DB connection is lost.  

This removes the need to call the back end whilst running tests.

It is a bad idea to rely on test data in databases. It becomes time consuming to set the data up and also makes it time consuming to debug the system. Also, with test data the team would need to be very careful about running test concurrently.

Golden Rule: Don't talk to the database during testing.

In Practice

The Extreme Web Architecture explained has been used to build a real commercial application. When the initial architecture was built during the development of the first user story it took 4 developers 2.5 weeks to build it using Quad Programming .

The first iteration of the application was quite small and had 8,500 lines of production code and 7,500 lines of test code. The tests for the architecture itself take about 2 seconds to run, and the full regression test suite takes 18 seconds.

The key to getting the tests running fast was removing the need to fire up the browser to test the rendered Html, whilst ensuring we could get full test coverage. Further enhancements were gained by mocking out the persistence layer.

Acknowledgements

The architecture defined herein is not solely my own work, but as a result of the 4 people on the team whose combined expertise resulted in the solution described. Those people were: Jason Gorman and his excellent knowledge of application architecture and software engineering. Duncan Green’s with his passion for superb OO design. Jason Hales and his ability for balancing complex design and pragmatism. And myself who has an almost unhealthy obsession with application testability.

Closing Comments

In this article I’ve discussed the need for ensuring that the suite of tests runs fast in an XP environment. I’ve qualified the concept of an Extreme Architecture and an Extreme Application.

I’ve also described the design of a real application that has been built using these principles.

Dave Chaplin

All content © Byte-Vision Limited 1997 - 2004