Using the AspNetDevelopmentServer attribute in combination with TeamBuild
Just two statements:
From this we can derive the following statement:
 
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:
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(@"Current folder: (?<f>.*).", RegexOptions.Multiline); var match = exp.Match(output); Assert.IsTrue(match.Success, "Expected output about current server folder not found."); // Write it to the context so we are able to read. TestContext.WriteLine(match.Groups["f"].Value); } /// <summary> /// Helper method for download a page from a specific uri. /// </summary> /// <param name="uri">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("SomeIdentifier", "Implementation")] public void TestMethod2() { var uri = (Uri)TestContext.Properties["AspNetDevelopmentServer.SomeIdentifier"]; 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: