ASP.NET - How do you Unit Test WebControls?

2020-04-02 07:28发布

问题:

Alright.

So I figure it's about time I get into unit testing, since everyone's been banging on about it for long enough. I've installed NUnit and gone through a few "intro to unit testing" type tutorials.

I'm currently putting together a small framework to help with the rebuild of one of our web apps, so I've created a VS2008 project for my framework and I want to unit test it as I go.

How on earth do I go about unit testing the WebControls? The methods are all protected or private, and since it's a framework, there isn't much else but WebControls.

Any pointers?

Burns

回答1:

You can do model-view-controller or model-view-presenter type architectures without using a full blown framework. You already found out that unit-testing ui-components is difficult. There are ways around that but you probably don't want to go that route. Usually this will make your tests very hard to maintain, more maintenance nightmare's is something programmers can do without :-)

Try to separate out the functionality you want to test in a "controller" or "presenter" class. Then test that class. To make it more testable you can hide the usercontrol class (the view) behind an interface and make the controller or presenter talk to the view through the interface. That way you can mock up the view in your tests.

I know this sounds like a lot of work and it seems like a workaround but if you get used to this it's a realy nice architecture that makes it far easier to change ui behaviour. You can always start using a "real" mvc framework when you realy need it :-)



回答2:

Ues the assembly:InternalsVisibleTo attribute and you'll be able to access those private members.

Put it in your webcontrol project's AssemblyInfo.cs (under Properties node)

[assembly:InternalsVisibleTo("YourTestProjectName")]


回答3:

You have found the biggest pain point of ASP.NET. As far as sealed, private classes that hinder unit testing.

This is the main reason that TDD people will use a MVC framework (ASP.NET MVC, Castle MonoRail) as it provides a clear seperation from your view templates and your controller logic. The controllers are fully testable.



回答4:

You could also look at testing components through the browser as a user would see them using a testing framework such as WebAii. I've seen it work and its pretty cool. I've also been told you can plug it into automated builds but I've not seen that as of yet.

Hope it helps ...



回答5:

This is an old article by now, but I was using NUnitASP to write nunit tests for asp.net WebControls in 2004. That article gives a detailed example of testing a simple control using their concept of creating a corresponding "Tester" class that encapsulates the details of your control from you tests. The Tester can (should) also be in the same assembly as your control so can share some things between them (e.g. utility functions, constants, etc.).

I used the technique (and others use variants of the technique) still today to test very sophisticated controls.

I hope that is helpful.



回答6:

The MVC framework mentioned above is the best way to test what the control does. However testing how it works is a bit different.

This is totally off the cuff but you could make the user control expose some protected methods and properties to return validation information and then have a testing user control inherit it. That control could populate fields, press buttons and what not. Kind of messy but it could work.



回答7:

You can also take a look at this Rhino Igloo framework. It is a compromised MVC framework for WebForms.



回答8:

Ivonna can test WebControls in isolation, within the Asp.Net context Just call session.GetControl("Path.ascx") and verify that it has all necessary properties.



回答9:

You test them like this:

[Test]
public void ConditionQueryBuilderTest_RendersProperHtml()
{
    var sw = new StringWriter();
    var queryBuilder = new ConditionQueryBuilderStub
    {
        ID = "UnitTestbuilder",
        QueryBuilderURL = @"\SomeAspxPage\SomeWebMethod",
        ResetQueryBuilderURL = @"\SomeAspxPage\OnQueryBuilderReset",
        FilterValuesCollection = new Dictionary<int, string> { {15, "Some Condition"}}
    };
    queryBuilder.RenderAllContents(new HtmlTextWriter(sw));

    AppendLog(sw.ToString());

    Assert.AreEqual(ExpectedHtml, sw.ToString()); // ExpectedHTML is the raw expected HTML
}

Here is my stub:

internal class ConditionQueryBuilderStub : ConditionQueryBuilder // ConditionQueryBuilder is a WebControl
{
    internal void RenderAllContents(HtmlTextWriter writer)
    {
        RenderContents(writer);
    }
}