Main image
6th May
2010
written by Chris

Image Credit: PixelPlacebo via Flickr and Creative Commons

Unit testing is good; test driven development is better.  As Knuth once famously quippedBeware of the above code. I have only proven it correct, not tested it.” There really is no substitution for good, solid testing.

Unfortunately, at least in C#, webpages don’t like to be unit tested.  I approach this post with an uncomfortable realization that I am about to lay out the issues and problems I’ve had while the solution I am presently using is far from satisfactory.

There is a right way and a wrong way to undertake a lot of unit testing.  If you want to learn about that, I recommend this post by Ryan Hagan as a great jumping off point.  My problem is not in writing good unit tests, however, so much as having a place to write them.

Axiomatic truth of Unit Testing: Your test code should exist in a different place than your production code.  This just makes good sense.  You don’t want to publish your tests every time you push code out the door and, since you want all of your published code to be tested you don’t want to have to write tests for your tests.  In C# the way one generally accomplishes this is the creation of a separate test project within the current solution.  That way the solution contains both your code and your tests.

Now generally, when you’re writing tests for C# this isn’t a big deal.  Since your project gets bundled up every time you build you can add a reference to the Test project that points at your main project; that way, the test harness knows what to make of all the custom types and whatnot that your various tests will have to evaluate.

This does not work for webpages.

When C# builds a webpage there’s no handy-dandy compiled thing at which to point your test code.  That means your test code has no idea what to make of any custom objects you’ve created and that makes any meaningful testing pretty much impossible.  Consider the following short example:

public void CreateTest()
        {
            string WidgetName = string.Empty;
            Widget expected = null;
            Widget actual;
            actual = WidgetFactory_Accessor.Create(WidgetName);
            Assert.AreNotEqual(expected, actual);
       }

In this example, “Widget” and “WidgetFactory” are both custom types.  C# has conveniently generated an accessor for my Factory but it has no reference to my main project and thus doesn’t know what a Widget is.  Consequently, this test won’t compile and I can’t run it.

There is a work-around solution I’ve found wherein I publish the Main Project and then create a reference from the Test Project to the published AppCode.dll file.  This works but requires that I publish the DLL every time I wish to re-evaluate my source code which makes the entire testing process prohibitively time consuming.

My suspicion at this point is that what I am trying to do is in fact neither possible nor recommended and that unit testing of a C# webpage should be accomplished by creating a sharp distinction between the interface and presentation layers, testing the presentation layer traditionally and then using interface testing suites like Watir.  This seems the sort of thing that should be verbosely stated somewhere if it is, indeed, the case.

Update

As per Ryan’s suggestion below and some other feedback I’ve gotten I am moving much of the logic code out of the project and creating a project-level distinction between the interface and the presentation layers of the application.  This should allow me to more sanely test the presentation layer and leverage Watin in to handle the interface testing.

It’s not the sort of separation recommended, but the architecture on this particular system is fairly dated and a full out re-factoring is out of the question (for now).

As it’s conspicuously missing from many other write-ups I’ve seen I’ll say it here.  You can not meaningfully test anything except the interface of a C# web project.  If you want to test your code, move it into a DLL project and allow your web project to call that DLL.  Ideally the Web project should be as thin as possible.

3 Comments

  1. Ryan
    06/05/2010

    Your suspicion is correct. You’re not using the recommended method for writing web page code. First, I suspect that you’re using a WebForms style of architecture, which is definitely *not* recommended now that .Net MVC is mature. Since changing the architectural style of your application is a bit of an undertaking, I’ll try to make my advice a little more relevant to your predicament.

    The answer is to separate out the layers of your app in a better way, and start making use of Mocking. You’ll definitely need to do a little refactoring, but you can keep this manageable. For a really great, and detailed, discussion on best practices when refactoring “legacy” code, pick up a copy of Working Effectively with Legacy Code by Michael Feathers.

    Now, some advice. I realize that the code you posted is example, throw-away code, but I want to look at that first, because it may shed some light on what you’re trying to accomplish and maybe get you to start thinking about the problem from a different direction.

    The name of your test “CreateTest” is meaningless. When you’re looking at your test results and you see “CreateTest failed”, that doesn’t tell you anything. A better name for that test would be “TestThatDefaultWidgetIsCreatedWhenEmptyWidgetNameIsPassedToFactory”.

    Unless your factory has a lot of conditional logic in it, there’s not a lot of value in testing said factory. You want to spend more of your time testing the different widget objects. If I had a look at your test, I have a feeling I’d see some higher level tests in there along side of that factory test. You’re probably testing at too high of a level. There’s nothing wrong with that, in fact, I highly encourage that, but be careful that you’re not mixing unit and integration tests in the same fixture.

    Now, addressing the comment that “‘Widget’ and ‘WidgetFactory’ are both custom types” and that your project “doesn’t know what a Widget is” is a different matter. You need to start making use of interfaces and polymorphism in your code. Your code shouldn’t ever depend on a specific implementation of a widget, it should depend on an interface. What you have there is a classic case of high-level code knowing too much about implementation details. Fixing this problem with the way the code is “wired up” will also address the problem you have with needing to compile anytime a small change is made a test needs to be rerun. You’ll only need a full compile when the interface changes, not the body of the methods.

  2. Chris
    06/05/2010

    Throw away code is right. I literally copied the first auto-generated stub that VisualStudio spat out and made a few quick change to it so it wouldn’t be obviously broken. All I really wanted to do was give the reader an indication of the production of a custom type that couldn’t be reached.

    The really long and descriptive names are a nice idea though; I’ll be sure to integrate them.

  3. […] This post was mentioned on Twitter by Ryan Hagan, tgkillfile. tgkillfile said: I'm having trouble setting up #unit-testing on a #CSharp webpage. Advice is welcome. http://bit.ly/9MlCZL #tdd #unittesting […]