OWIN Self-Hosted Test Server for Integration Testing of OData and Web API

A co-worker of mine and I were recently given a task to perform integration testing on OData and Web API services. You can view his posts on the subject in his series: Part 1, Part 2, and Part 3. Traditionally, one might mock the web requests and responses, but by using the TestServer found in Microsoft.Owin.Testing namespace, we can start an in-memory HTTP server for doing full integration tests. You can get the NuGet package here.

To start create a new Unit Testing project with MS Test and a new ASP.NET MVC / Web API project. In the ASP.NET MVC / Web API project install Web API 2.2 and Web API 2.2 for OData v4.0 and OData v1-3. For the unit test project, install Web API 2.2, Web API 2.2 for OData v4.0 and OData v1-3, Web API 2.2 OWIN Self Host, Web API 2.2 OWIN, and Microsoft.Owin.Testing. Below is a sample of a unit/integration test for setting up an OWIN test server for in-memory integration testing:

namespace SelfHosting.Test
{
    using Microsoft.Owin.Testing;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Owin;
    using SelfHosting.Test.Models;
    using System;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;
    using System.Web.Http;
    using System.Web.Http.Dispatcher;
    using System.Web.OData.Builder;
    using System.Web.OData.Extensions;
    using WebApp.Models;

    [TestClass]
    public class SelfHostingTest
    {
        protected TestServer server;

        [TestInitialize]
        public void Setup()
        {
            server = TestServer.Create(app =>
            {
                HttpConfiguration config = new HttpConfiguration();
                WebAppFacade.WebApiConfig.Register(config);
                app.UseWebApi(config);
            });
        }

        [TestCleanup]
        public void TearDown()
        {
            if (server != null)
                server.Dispose();
        }

        [TestMethod]
        public async Task TestODataMetaData()
        {
            HttpResponseMessage response = await server.CreateRequest("/odata/?$metadata").GetAsync();

            var result = await response.Content.ReadAsAsync<ODataMetaData>();

            Assert.IsTrue(result.value.Count > 0, "Unable to obtain meta data");
        }

        [TestMethod]
        public async Task TestWebApi()
        {
            HttpResponseMessage response = await server.CreateRequest("/api/values").GetAsync();

            var result = await response.Content.ReadAsStringAsync();

            Assert.AreEqual("\"Hello from foreign assembly!\"", result, "/api/values not configured correctly");
        }

        [TestMethod]
        public async Task TestODataPeople()
        {
            HttpResponseMessage response = await server.CreateRequest("/odata/People").GetAsync();

            var result = await response.Content.ReadAsAsync<ODataResponse<Person>>();

            Assert.AreEqual(result.value.Count, 3, "Expected 3 people to return from /odata/People");
        }
    }

}

The OData meta data is serialized into a POCO (Plain old C# object):

namespace SelfHosting.Test.Models
{
    using System.Collections.Generic;

    public class ODataMetaData
    {
        public string odatacontext { get; set; }
        public List<Value> value { get; set; }
    }

    public class Value
    {
        public string name { get; set; }
        public string kind { get; set; }
        public string url { get; set; }
    }
}

By using a generic ODataResponse class, we can deserialize our OData response into any POCO:

namespace SelfHosting.Test.Models
{
    using System.Collections.Generic;

    public class ODataResponse<T>
        where T : class, new()
    {
        public string odatacontext { get; set; }
        public List<T> value { get; set; }

        public ODataResponse()
        {

        }
    }
}

The beauty about using the TestServer is that it is self-contained and the HTTP server is inaccessible outside of the process. Once the tests complete, the server is shutdown. The WebApiConfig registered with the TestServer determines which controllers and routes to load for testing. No production code needs to be changed in order to test existing Web API and OData controllers. The only problem that I have found is that attribute routes don't seem to register correctly. Perhaps I have not found the correct method of registering the attribute routes for the TestServer.

Here is the Visual Studio 2013 solution with both a web project and a unit testing project:

SelfHostingUnitTest.zip (1.39 mb)

My thoughts on continuous integration

8. August 2011 17:29 by Cameron in Continuous Integration  //  Tags: , , , ,   //   Comments

Whether the choice is SVN or git or another version control system, I believe it is vital for software development groups to have a central source repository. My preferred version control system is git as it provides huge improvments over Subversion and you can work with a local repository without needing to affect the remote repository. With each commit or push to a team's central repository, it is important to check that the latest commit/commit doesn't break the main build. This is where continuous integration comes into play.

Continuous integration servers can attempt to build the source committed/pushed to the central repository and if the build passes, it can then integrate the changes into the main development branch. Some continuous integration servers will push the code to production once the code passes a series of unit tests and builds with the rest of the main development branch. However, if the build fails, the code will not be integrated into the main build and the failed build will be logged for reviewing. The event logging helps immensely with finding and fixing software bugs quickly, allowing the main branch to accept the changes made by the original user. This feature saves developers time and frustration in trying to sift through thousands of lines of code. Why should anyone manually check for a software bug if a computer can analyze the source code and find it for you?

With various continuous integration servers, project maintainers can view statistics such as commit/build success rate and code redundancy. In general practice, developers should never just copy and paste code. This is not coding. It's laziness. Usually, if you are copying and pasting code, you can probably refactor your code to use only one set of the code you were originally going to copy. There's no point having duplicate code in a code project if you can help it. One of the reasons it's a bad idea to copy and paste code is that the code base becomes harder to maintain. Another reason is that copied code doesn't necessarily work everywhere you paste it to. Just because it works in one place doesn't mean it will work as expected in another place.

With all of the benefits of continuous integration, I believe that every software development team should have some sort of continuous integration to track their projects whether they are an open source shop or Microsoft shop. Continuous integration really promotes the agile software development cycle and everyone should enjoy the advantages that it provides. I can definitely say that with all collaborated personal software projects that I work with I will make sure that continuous integration is key part of the development process.

 

Month List

Tag cloud