Creating NUnit Tests in Visual Studio 2013

Recently I’ve been interested in incorporating more automated testing in my projects. The benefits of having tests are heavily covered in other topics so I won’t waste time explaining why I sat in on a few sessions at this year’s Code Camp NYC focusing on automated testing. Needless to say a few of the sessions was all it took to push me into getting my feet wet.

I decided to start out on a demo project just so I didn’t dirty up any formal projects while I tried out the best approach to implementing the tests. I recently created a project to check out ASP.NET Web API as a progressive download host (which allows for streaming video to iOS devices, allows downloads to be chunked and resumed by download managers, etc.) This seemed like a good candidate for tests since the behavior of the library changes based on the HTTP request headers for a resource.

Configuring Visual Studio for NUnit Tests

I’m not entirely sure why one of the sessions focused on using NUnit rather than the built in test provider in Visual Studio but it worked, looked easy to configure and has name recognition so I decided to follow the same path. Luckily it’s very easy to get NUnit running in Visual Studio 2012 and 2013 thanks to a couple NuGet packages. (An additional benefit of which appears to be seamless test execution on your continuous integration server, a topic I have not even begun exploring for myself.)

To get tests up and running, start by creating a Unit Test project in your solution. Getting NUnit running is as simple as searching NuGet for “NUnit” and installing the following packages.

Adding a unit test project to a Visual Studio solution.

Add a Unit Test Project to your solution.

Adding the NUnit Packags to your Test Project

Install the NUnit NuGet Packages in your Test Project.

Once the NuGet packages are installed, you’re ready to start creating your tests by adding a Unit Test to your testing project. By default Visual Studio will insert template code for you based on the Microsoft tests. You can see this by the using statement for Microsoft.VisualStudio.TestTools.UnitTesting and the use of the TestClass and TestMethod attributes decorating the code. To use NUnit, just replace the using statement with “NUnit.Framework” and replace TestClass and TestMethod with TestFixture and Test, respectively. That’s all there is to it.

Update the using statement and attributes

Change the auto-generated code to reference NUnit’s library and testing attributes.

Implementing Tests

For my demonstration, I am interested in testing out the library I wrote to wrap the ByteRangeStreamContent. I want to make sure the library response appropriately when supplied with valid, invalid and no HTTP RANGE header as well as with a valid and invalid resource stream. Knowing what I wanted to test, I had to figure out the best way to write the tests. I considered trying to automate a browser or use a WebClient to generate my traffic but before I went through that effort, I decided just to see if the HttpRequestMessage and HttpResponseMessages themselves could be used. To my utter surprised, I was able to instantiate an HttpRequestMessage, populate the RANGE header with values and get the expected HttpResponseMessage in return.

A few lines of code was all it took to do a request for a mocked stream using a range request. With that working I proceeded to create 4 more tests to cover the other request types I wanted to validate.

Test Explorer shows the passing or failing of your tests

Passing Test Results!

It took a few tries to get the hang of checking the response for expected values but once the green checkmarks started appearing, I was hooked. Look at those execution times too. At this point I was pretty happy with my experiment but I kept poking around a little more.

Ensuring Complete Code Coverage

While looking through the Test menus I noticed there was an option to Analyze Code Coverage. Once again not expecting much, I let it run against all my tests and was greeted with a summary window. The results allowed me to drill down to see what specific components in my project were covered by tests and which weren’t. I was surprised to see my ProgressiveDownload library wasn’t 100% covered so I double-clicked on the line item and was taken directly to the source code complete with – somewhat hard to read with my dark color scheme – highlighting showing which paths in my code were not covered. That’s awesome! I quickly wrote another tests to validate this path and ran my tests again to verify they all passed and re-ran the Analyze Code Coverage option to see I was now covering 100% of the code I was targeting.

Use Analyze Code Coverage to ensure you test everything

Analyze Code Coverage to see if you’re testing everything in your code.

Highlighted code shows the paths not yet covered by tests

Double click a line item to be taken to highlighted source code showing paths not covered by tests. Here we see the general Exception catch isn’t covered.

Add more tests to ensure full code coverage

Add tests until your code is completely covered.

Updated code coverage results show 100% coverage

Rerun your code coverage analysis to make sure everything is covered.

My overall experience was quite positive and definitely a lot easier to integrate than I expected. I am definitely going to be pushing for more automated unit testing in the office and this is a great starting point.

Happy Testing,
-Erik

Reference Test Method Code

For those hoping to see how I wrote my tests, here is the code.

using NUnit.Framework;
using System.IO;
using System.Net.Http;
using WebApiVideoStreamer;

namespace UnitTestDemo
{
    [TestFixture]
    public class ProgressiveDownloadTests
    {
        [Test]
        public void TestProgressiveDownloadWithoutRangeRequest()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = null;

            var ms = new MemoryStream(_DummyData);
            ms.Seek(0, SeekOrigin.Begin);

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                Assert.AreEqual(System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream"), result.Content.Headers.ContentType);
                Assert.AreEqual(30, result.Content.Headers.ContentLength);
                Assert.AreEqual(System.Net.HttpStatusCode.OK, result.StatusCode);
            }
        }

        [Test]
        public void TestProgressiveDownloadWithRangeRequest()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(0, 5);

            var ms = new MemoryStream(_DummyData);
            ms.Seek(0, SeekOrigin.Begin);

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                Assert.AreEqual(System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream"), result.Content.Headers.ContentType);
                Assert.AreEqual(6, result.Content.Headers.ContentLength);
                Assert.AreEqual(System.Net.HttpStatusCode.PartialContent, result.StatusCode);
            }
        }

        [Test]
        public void TestProgressiveDownloadWithOpenRangeRequest()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(5, null);

            var ms = new MemoryStream(_DummyData);
            ms.Seek(0, SeekOrigin.Begin);

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                Assert.AreEqual(System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream"), result.Content.Headers.ContentType);
                Assert.AreEqual(_DummyData.Length - 5, result.Content.Headers.ContentLength);
                Assert.AreEqual(System.Net.HttpStatusCode.PartialContent, result.StatusCode);
            }
        }

        [Test]
        public void TestProgressiveDownloadWithMultipleRangeRequest()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(0, 4);
            request.Headers.Range.Ranges.Add(new System.Net.Http.Headers.RangeItemHeaderValue(10, 14));

            var ms = new MemoryStream(_DummyData);
            ms.Seek(0, SeekOrigin.Begin);

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                var multipartStart = "multipart/byteranges; boundary=";
                var responseContentType = result.Content.Headers.ContentType.ToString().Substring(0, multipartStart.Length);

                var resultContent = result.Content.ReadAsStringAsync().Result;

                Assert.AreEqual(multipartStart, responseContentType);
                Assert.AreEqual(System.Net.HttpStatusCode.PartialContent, result.StatusCode);
            }
        }

        [Test]
        public void TestProgressiveDownloadWithBadRange()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(50, 100);

            var ms = new MemoryStream(_DummyData);
            ms.Seek(0, SeekOrigin.Begin);

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                Assert.AreEqual(System.Net.HttpStatusCode.RequestedRangeNotSatisfiable, result.StatusCode);
            }
        }

        [Test]
        public void TestProgressiveDownloadWithBadResource()
        {
            HttpRequestMessage request = new HttpRequestMessage();
            request.Headers.Range = null;

            MemoryStream ms = null;

            var vidStreamer = new ProgressiveDownload(request);
            using (var result = vidStreamer.ResultMessage(ms, "application/octet-stream"))
            {
                Assert.AreEqual(System.Net.HttpStatusCode.BadRequest, result.StatusCode);
            }
        }

        public ProgressiveDownloadTests()
        {
            _DummyData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        }

        byte[] _DummyData;
    }
}
Advertisements

One response to “Creating NUnit Tests in Visual Studio 2013

  1. Pingback: Web Api Progressive Download on GitHub and NuGet | Erik Noren's Tech Stuff

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s