Unit testing code behinds in ASP.Net Web Forms

13 June, 2010 § 7 Comments

Have you ever inherited an ASP.Net website with waaaaaaay too much logic in the code behinds?

A while back I came up with a solution to solving this problem. I don’t have a name for it, but if you do, please leave a comment at the end of this post.

The problem that I was trying to solve is that I wanted to make a new page and implement it using Test Driven Development. The hard part was that there was a lot of code that basically dealt with the visual aspect and interaction of the page but not much with the data model. I needed to find a way to write tests around this code, as the data model code already had a framework for writing tests.

Here is an example of the before-case:

public class CodeBehind : System.Web.UI.Page {
   protected void btnFindUser_Click(Object obj,  EventArgs e) {
      if (Page.IsValid) {
         var dbConnection = new DatabaseConnection();
         var users = dbConnection.GetUsers( txtQuery.Text );
         if ( !users.empty() ) {
            dataTable.Bind(  users );
         }
      }
   }
}

What I came up with was an introduction of a “controller” class that lived outside of the ASP.Net Web Forms project. Here is an example of the after-case:

// CodeBehind.aspx.cs
public class CodeBehind : System.Web.UI.Page {
   protected void btnFindUser_Click(Object obj,  EventArgs e) {
      controller.FindUser( Page.IsValid, txtQuery.Text, dataTable );
   }
}

// CodeBehindController.cs
public class CodeBehindController {
   public void FindUser( bool pageIsValid, string query, DataTable dataTable ) {
      if ( pageIsValid ) {
         var dbConnection = new DatabaseConnection();
         var users = dbConnection.GetUsers( query );
         if ( !users.empty() ) {
            dataTable.Bind( users );
         }
      }
   }
}

// CodeBehindControllerTests.cs
public class CodeBehindControllerTests {
   public void FindUser_WithNoUsersFound_DataTableIsEmpty() {
      DataTable table = new DataTable();
      CodeBehindController controller = new CodeBehindController();

      controller.FindUser( true, "ImpossibleUserToFind", table );

      Assert.IsTrue( table.empty() );
   }
}

With that, I can now mock out the DatabaseConnection and the DataTable and I’m good to go. By passing in Page.IsValid instead of the whole Page object, I’m able to provide types that are easier to instantiate and I also now have code with less dependencies.

Getting this to work is actually very simple. Just introduce another class and put all of your logic in that class. There shouldn’t be a single conditional in your *.aspx.cs page. As long as you follow this pattern, it should be pretty easy to TDD your next ASP.Net Web Forms page.

Any questions? Leave a comment and I’d be glad to help.

Tagged: , , ,

§ 7 Responses to Unit testing code behinds in ASP.Net Web Forms

  • A.J. says:

    Hey Jared! Yes! Separation of logic and the actual web page is a good approach! And this doesn’t just pertain to web pages but applications as well. Separating stuff out can mean more typing but overall is for the best! I also like how you called it controller; I did something similar and called it the same thing.

  • msujaws says:

    Very cool that you were thinking the same thing.

    I think the default out-of-the-box implementation of Web Forms lends itself to developers putting too much logic in untestable areas. The recent developments of ASP.Net MVC will help move new projects, but there needs to be a solution to legacy projects that will not be able to move to MVC in the near future.

    I’m confident that such a simple trick as this can help developers move their legacy code in to a testable state.

  • msujaws says:

    @ivowiblo Thanks for the link. I’ve used MVC before and I thought that this would be a natural way to use the pattern within existing ASP.net websites. What do you think about the solution that I implemented?

  • ivowiblo says:

    It’s useful for event based UIs. The main benefit or using MVP is the inversion of the control: the presenter handles all the UI flows and you are allowed to intercept the calls to the presenter for logging, transactions, etc. Also, you can test the flows. For example, for testing the following rule: “In a login page show an error message if the login is invalid”.

  • Gary says:

    Been awhile since you wrote this Jared. Any updates? With more extensive use are the results still to your liking? I’ve just inherited an ugly web forms app and giving some thought on how best to get some test coverage on it. Thanks.

    • msujaws says:

      I no longer work on that codebase anymore, so I don’t know how it has aged over time. Although I am still in favor of this design pattern.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

What’s this?

You are currently reading Unit testing code behinds in ASP.Net Web Forms at JAWS.

meta

%d bloggers like this: