Channels ▼
RSS

Testing

ASP.NET and Testability: An Uneasy Relationship


Testability has been recognized as one of the fundamental attributes of software since 1991, according to ISO/IEC 9126, a standard that you'll find well summarized on Wikipedia. However, a decade ago when the ASP.NET framework made its debut, the whole theme of testability was not as popular as it is today, at least among developers focusing on Microsoft technologies. One of the key takeaways of ASP.NET Web Forms was "focus on the business logic and let the framework deal with what browsers expect." Gaining abstraction over typical elements of a Web solution such as HTML, JavaScript, and CSS was a specific goal of ASP.NET Web Forms.

Developers were given server controls to quickly and effectively arrange views, and code-behind classes to serve incoming requests and produce appropriate responses. A code-behind class is a piece of code that is tightly bound to the ASP.NET infrastructure. Any code placed in a code-behind class has direct access to the HTTP context and can read and write cookies, posted data, query string parameters, session state, HTTP headers, and so forth. Code-behind classes serve two main purposes: For HTTP GET requests, they set up the page for display; for HTTP POST requests, they grab posted data, prepare a call to the application's back end, receive a response, and prepare the next view for the user.

Sounds like a simple workflow, right? But in fact, it is not necessarily simple. The real complexity of such a workflow is determined by the inherent complexity of the call made to the application's back end. It is one thing to invoke a stored procedure from the code-behind class and display its results; it is quite another to invoke a stored procedure that is only one step in a far more complex piece of business logic. As an example, consider what it means for an e-commerce application to place an order. There might be extremely simple scenarios in which all that is required is adding a few records in a couple of tables; say, one record in the OrderDetails table for each item ordered, and one record for the order itself in the Orders table. In such a case, it might be acceptable that developers place all the code — a single call to the Data Access Layer — right in the code-behind class.

What if, instead, placing an order involves several (sequential and conditional) steps such as checking the credit status of the customer, checking the availability of goods, refilling stock by placing orders to suppliers, interacting with the back end systems of the shipping company, updating the company's accounting systems, and maybe more? You might still need to add a bunch of records to the Orders and OrderDetails tables, but there's now a whole workflow to be orchestrated, which significantly increases the level of complexity of the code you need to trigger from the user interface.

Can this orchestration code be managed from within code-behind classes?

Over the past few years, scenarios where the code required to serve a Web request is fairly complex have become ubiquitous. As a result, having fat methods in code-behind classes shifted from being an acceptable practice to being a bad practice best avoided for the sake of maintainability and testability.

More complexity in the logic expressed via software inevitably means a stricter need to test the code to ensure that changes and new development don't break existing features.

How Easy Is It To Test Code Written for the ASP.NET Framework?

Design for Testability is a methodology aimed at making software easier to test automatically through other software. Design for Testability is centered on three pillars: visibility, control, and simplicity. Code that is easy to test is code that exposes the most relevant parts of the internal state so that appropriate assertions can be written and checked. Likewise, for tests to be relevant and reliable, testers should be able to force ad hoc input values on methods and exercise control over their invocation. Finally, the simpler the code, the more reliable response you get from the test.

The main impact of testability on software development comes through unit tests and integration tests. Unit tests are automatic tests done for the most part in isolation on code that has no dependencies whatsoever on the surrounding environment. Integration tests instead check whether interconnected parts of the system work well together.

When you try to apply Design for Testability to ASP.NET Web Forms, you find that you can gain a good level of visibility over the state of the infrastructure (that is, ASP.NET intrinsic objects), but you can hardly force test values without spinning up the entire ASP.NET runtime in the test environment. In fact, in ASP.NET Web Forms, you can't just simulate a fake HTTP context with test values in session state, a response stream that saves to memory, or an ad hoc query string. Because code-behind classes are tightly bound to the ASP.NET HTTP runtime, and the ASP.NET HTTP runtime doesn't allow mocking, covering ASP.NET Web Forms applications with unit tests is pretty hard. Integration tests then seem to be the only affordable option you have for testing ASP.NET Web Forms pages. But integration tests, by nature, are slower to run, thus developers tend to run them less often.

What Can You Do About It?

Unit testing is quite problematic in ASP.NET Web Forms if developers place all of the use-case logic into code-behind classes. An alternative option would be introducing a new layer of code that bridges code-behind classes (still part of the presentation layer) to the back end of the system. Such intermediate classes would receive HTTP context-specific data as an argument and operate in total isolation from the surrounding HTTP context. As a result, you extract all code that really expresses application logic from the presentation layer. The resulting design is therefore cleaner and much more testable.

So the bottom line is that while ASP.NET Web Forms certainly was not laid out with testability in mind (it was designed at a time when testability was not a primary concern for most developers), by intensively applying good principles of software design (Separation of Concerns and layers) you can easily write ASP.NET code that is unit testable.

On a final note, let's consider the twin ASP.NET framework — the increasingly popular ASP.NET MVC. Being a newer framework, ASP.NET MVC was designed for helping developers to write more testable code. And those good old principles of software design applied to ASP.NET MVC can make not just your code but also your unit tests far simpler.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video