Using the AspNetDevelopmentServer attribute in combination with TeamBuild

Just two statements:

  • Everyone should unit test (or unit integration test, for the purists)
  • Everyone should use TeamBuild (or another nightly build infrastructure)
  • From this we can derive the following statement:

  • Everyone should unit test in combination with TeamBuild!
  •  

    With this in mind I had one little problem regarding the AspNetDevelopmentServer attribute. This attribute allows you to host your web site or service during test execution. The problem I had is that the output directory of the System Under Test (SUT) will be different during the nightly build compared to a local run from Visual Studio. Jim Lamb once wrote an article about it using the solution directory as a base path reference. But this is not a solution for us because we are redirecting the output to a specific directory in the build. But now I found out that it is possible to use the name of the referenced project and MSTest translates this to the location of the published website!

    Consider the following application:

    image

    I have an Implemenation project and an Implementation.Test project where the latter references the first. I slightly modified the default.aspx to print the directory it is running from:

    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Label1.Text = string.Format(CultureInfo.InvariantCulture, "Current folder: {0}.", Server.MapPath(""));
        }
    }

    Then I wrote the following test code:

    [TestClass]
    public class UnitTest1
    {
        public TestContext TestContext { get; set; }
    
        [TestMethod]
        // The first argument is just an identifier that we will use to retrieve the location from the test context.
        // The second argument is the name of the web application project that is referenced.
        [AspNetDevelopmentServer("SomeIdentifier", "Implementation")]
        public void TestMethod1()
        {
            // Retrieve the url of the hosted site from the TestContext and download the page.
            var url = (Uri)TestContext.Properties["AspNetDevelopmentServer.SomeIdentifier"];
            var output = DownloadPage(url);
    
            // Simple check on output.
            StringAssert.Contains(output, "Welcome to ASP.NET!", "Expected welcome string not found in output.");
    
            // Check the current working folder which should be outputed by the page.
            var exp = new Regex(@&quot;Current folder: (?<f>.*).&quot;, RegexOptions.Multiline);
            var match = exp.Match(output);
            Assert.IsTrue(match.Success, &quot;Expected output about current server folder not found.&quot;);
    
            // Write it to the context so we are able to read.
            TestContext.WriteLine(match.Groups[&quot;f&quot;].Value);
        }
    
        /// <summary>
        /// Helper method for download a page from a specific uri.
        /// </summary>
        /// <param name=&quot;uri&quot;>The uri of the page.</param>
        /// <returns>The page content as a string.</returns>
        private static string DownloadPage(Uri uri)
        {
            var request = HttpWebRequest.Create(uri);
    
            using (var response = request.GetResponse())
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        }
    }

    Remark: Of course a coded UI or web test could be a better alternative for this solution!

    This can of course also be used to host a WCF service in a Web Application and generate a client in the test project to access it. Bill Wang published a blog about redirecting the generated client. I refactored his code somewhat by using extension methods, generics and the EndPointAddressBuilder. Catching exceptions was for me a no-go because I like the details in my test log.

    static class WcfExt
    {
        public static void UrlRedirect<T>(this ClientBase<T> client, Uri uri)
            where T : class
        {
            EndpointAddressBuilder builder = new EndpointAddressBuilder(client.Endpoint.Address);
            builder.Uri = new Uri(client.Endpoint.Address.Uri.OriginalString.Replace(client.Endpoint.Address.Uri.Authority, uri.Authority));
    
            client.Endpoint.Address = builder.ToEndpointAddress();
        }
    }

    Now I can test my service like this:

    [TestMethod]
    [AspNetDevelopmentServer(&quot;SomeIdentifier&quot;, &quot;Implementation&quot;)]
    public void TestMethod2()
    {
        var uri = (Uri)TestContext.Properties[&quot;AspNetDevelopmentServer.SomeIdentifier&quot;];
        var client = new ServiceReference1.Service1Client();
        client.UrlRedirect(uri);
        client.DoWork();
    }

    It is even possible to enable Code Coverage and get results in both a test run from Visual Studio and from the nightly build:

    image