Thursday, May 17, 2007

Testing ASP.NET pages outside Http Context

Phil Haack had a great post on testing ASP.NET pages with embedded Visual Studio Development Web Server. This is a cavalry and heavy artillery of ASP.NET testing. Once you established a decent testing framework based on that code, you are covered for your UI part.

I've never got to that point (I have an excuse - I am not a UI guy!) but some requirements for UI implementation - like validation - are perfectly eligible candidates for unit testing. Chris Stevenson gave me an idea (and what I finally tried) to test an ASP page with brutal force - just like any other class. We'll start from two assumptions: the validation on the page is done by standard Validation controls; properties of all Validators (in particular - ControlToValidate) are set in the code-behind (in our example - in the SetupValidators() method).

Next we'll proceed in three steps:

Step 1. On the runtime we do not have Page.Controls collection populated for us, so we will have to do it manually. With the following code our controls will be set and ready for the test.

    public static class PageLoader    {
        public static void ResurrectControls(ref Page myPage)   {

BindingFlags flags =

BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

            foreach (FieldInfo field in myPage.GetType().GetFields(flags))   {
                if (typeof(Control).IsAssignableFrom(field.FieldType))    {
                    Control toAdd = null;
                    ConstructorInfo constructor = field.FieldType.GetConstructor(new Type[0]);
                    if (constructor != null)  {
                        toAdd = (Control)constructor.Invoke(new object[0]);
                        toAdd.ID = field.Name;
                        field.SetValue(myPage, toAdd);
                        myPage.Controls.Add(toAdd);
                    }
                }
            }
        }
    }

Step 2. Let's create a base TextFixture class :


    [TestFixture]
    public abstract class ValidatorBaseTestFixture   {
        protected Page testPage;
        public abstract void SetTestedPage();
 
        public virtual T PageFactory<T>() where T : Page   {
            ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]);
            return constructor.Invoke(new object[0]) as T;
        }
 
        [SetUp]
        public virtual void SetUp()   {
            SetTestedPage();
            PageLoader.ResurrectControls(ref testPage);
            ((ITestableValidatorContainer)testPage).SetupValidators();
        }
    }

Step 3. The concrete unit test class inherits from the base fixture and implements an abstract method SetTestedPage :


    [TestFixture]
    public class MyPageTests : ValidatorBaseTestFixture   {
        public override void SetTestedPage()     {
            testPage = (Page) PageFactory<MyPage>() ;
        }
 
        [Test]
        public virtual void SetUp()  {
            testPage.txtEmail="invalid$&@nothingcom";
            testPage.Validate();
            Assert.IsFalse(valEmailValidator.IsValid);
        }
    }

Now developers can write unified maintainable tests against validation on every particular page.

As a step further we can introduce different kind of testable interfaces and decorate our Base Page class. Example of this can be ITestableValidatorContainer interface (actually used in the TestFixture example above):
public interface ITestableValidatorContainer {
    public void SetupValidators();
}

No comments:


© 2008-2013 Michael Goldobin. All rights reserved