*This is the second post in a series about web test extensibility points.  The first post was about extending web tests using custom IHttpBody classes.*

It is a common practice for a web application to trap errors and redirect the user to a "We're sorry, an error has occurred..." page.  Unfortunately, these error pages often return a "200 OK" status code instead of an error code in the 400 or 500 range.  This behavior is definitely something to watch out for when creating and running web load tests since it could allow web application errors to go unnoticed.

Here is a sample validation rule called ValidateResponseUrl that checks response URLs to catch redirects to error pages:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;

namespace OcracokeSamples {
    public class ValidateResponseUrl : ValidationRule {
        private string _urlStringToFind = String.Empty;
        private bool _failIfFound = true;

        [Description("If true, validation fails if the specified string is found in the response URL.  If false, validation fails if the specified string is not found in the response URL.")]
        public bool FailIfFound {
            get { return _failIfFound; }
            set { _failIfFound = value; }
        }

        [Description("The string to search for in the response URL.  For example, enter 'Error.aspx' if you want to make sure the server does not redirect to that error page.")]
        public string UrlStringToFind {
            get { return _urlStringToFind; }
            set { _urlStringToFind = value; }
        }

        public override string RuleName {
            get { return "Validate Response URL"; }
        }

        public override string RuleDescription {
            get { return "Verifies the response URL.  This rule can be used to make sure a redirect to an error page does not occur, for example."; }
        }

        public override void Validate(object sender, ValidationEventArgs e) {
            //make sure the string to find has a value
            if (String.IsNullOrEmpty(_urlStringToFind)) {
                throw new ArgumentException("The UrlStringToFind property cannot be null or empty string.");
            }

            bool found = e.Response.ResponseUri.OriginalString.IndexOf(_urlStringToFind, StringComparison.OrdinalIgnoreCase) != -1;
            string foundMessage = found ? "was" : "was not";

            //set the result message that will appear in the web test viewer details tab and the load test error table
            e.Message = String.Format("The string '{0}' {1} found in the response URL.", _urlStringToFind, foundMessage);

            //set whether the validation passed or failed
            e.IsValid = found != _failIfFound;
        }
    }
}

As you can see, it doesn't take much code to implement this very useful validation rule.  I just added a new class to my test project, made it inherit from ValidationRule, and implemented the RuleName and Validate members.  The RuleDescription property and the [Description] attributes make custom validation rules easier to use by providing help text that shows up in the Add Validation Rule dialog (shown below) and the Properties window, but they are completely optional.

Looking at the Validate method, you might be scratching your head wondering how WebTestResponse.ResponseUri can be different than WebTestRequest.Url.  The answer is that when a request's FollowRedirects property is set to True, validation and extraction rules are automatically moved from the original request to the subsequent redirect request.

To use a custom validation rule in a web test, you must first let your test project know it exists.  If the rule class is included in the test project, just build the project and the rule will show up in the Add Validation Rule dialog.  If, however, the rule is part of a separate class library project and potentially shared by multiple test projects, just build that class library project and add a reference to it in your test project(s).  Now when you right-click a request and select Add Validation Rule, you should see your rule in the Add Validation Rule dialog shown below.

Add Validation Rule dialog

You can see in this case that I'm going to cause the request to fail validation if the server redirects to error.aspx.  If you have multiple error pages, you would set the FailIfFound property to False and enter the correct response URL in the UrlStringToFind property so validation will fail if the string is not found in the response URL.

Please let me know if you have any questions or comments about this validation rule or custom validation rules in general.  I'm also open to ideas for what the next web test extensibility point example should be.