Integration Testing with Unit Tests and MSTest

Visual StudioIn many of the projects that I have worked on, the application we’re building needs to integrate with a back-end system or a web service layer that is maintained by a third party or by another team.  In these cases, we shouldn’t assume we’ll have the ability to ensure that the other group has set up an automated test bed to verify regression.  Especially if that other system is having changes made to it to support the project!

This is where integration testing comes into play.  There are a few things that you should be doing with your unit tests to ensure your integrations continue working throughout the lifetime of your project, and also that your automated tests are running efficiently so that your team is not slowed down waiting on expensive integration unit tests that are running for long periods of time.

1. Use Mock Objects

External systems go down, and usually their response times are slow even when they are up.  If you have business logic that you need to run automated tests on, and those tests depend on data from another system, these tests will start to fail when that other system goes down or becomes unresponsive.  Instead, you should cut that system out of your unit tests by using mock objects.

If you build some utility methods that can construct common response scenarios that you’ll need, you can then execute your unit tests with these mock objects instead of depending on the availability of the other system to serve up the data.  This also allows you to validate that your business logic can properly process the results of the integration without the overhead of actually executing against the other system.

Here is an example of a unit test that mocks the data from the back-end system to ensure the cart object can manipulate them correctly:


/// <summary>
/// Create a cart with one product and then update the quantity of that
/// product by adding the same product to the cart (the quantity of the 
/// existing cart item should increase but not add another line item)
/// </summary>
[TestMethod(), TestCategory("Cart")]
public void UpdateProductInCartTest()
{
   //Create a mock object of the data from the back-end web service
   decimal price = new decimal(275.00);
   var product = new Product() { Id = 12345, Price= price, Name="Test Product" };

   //Create our test data that is from the local application
   var cart = new Cart();
   cart.Add(product);

   Assert.AreEqual(1, cart.Count, "Initial add to cart failed.");
   Assert.AreEqual(price, cart.Total, "Initial price is incorrect.");

   //Add the item again and double the quantity
   cart.Add(product);

   decimal expectedPrice = new decimal(550.00);
   Assert.AreEqual(1, cart.Count, "Count did not remain at one after second add.");
   Assert.AreEqual(expectedPrice, cart.Total, "Total did not double when adding a second time.");
}

2. Write integration tests for regression

Even though we’d love to assume everything will always work, in integration projects this is rarely the case.  A seemingly unrelated change can have widespread consequences when you are working with the integration of multiple systems.  For this reason you should always have a bed of tests that ensure that the systems you are integrating with will act as you expect.

An easy way to do this is to make sure you have a manager or proxy layer in your application that deals with communicating with the back-end systems.  If this layer accepts object inputs, you can then easily construct calls to the back-end system in your unit tests and verify that the response is correct.

Here is an example of an integration test that ensures a back-end system returns the expected data, given a specific scenario:


/// <summary>
/// Tests if a user has already purchased a product
/// <summary>
[TestMethod(), TestCategory("Cart")] 
public void TestAlreadyPurchased() 
{ 
   const int userId = 12345; 
   const int productId = 12345;
   bool expected = true; 

   //Run the integration test
   bool alreadyPurchased = CartManager.AlreadyPurchased(userId, productId);

   //Check to see if we got our expected result 
   Assert.AreEqual(expected, alreadyPurchased);
}

3. Use TestCategory to exclude your integration tests from health checks

If you’re like me and you run your unit tests every check-in as part of a Continuous Integration build definition, you will notice a significant slow-down in these builds as you add in regression integration tests. Over time, it gets worse and worse and you’ll be tempted to break out your unit tests to run in a separate build. This is where the TestCategory attribute comes into play.

By tagging a unit test with a category, you can alter your build definition to ignore it. This way you can continue to run your normal unit tests as part of your continuous integration build, but you can then also setup a separate regression build definition to run everything nightly, or every 15 minutes, or whatever frequency works for your team. The first step is adding the category to the unit test. In the below example, we’ll take the above unit test definition and add a category to it named “Integration”.


/// <summary>
/// Tests if a user has already purchased a product
/// <summary>
[TestMethod(), TestCategory("Cart"), TestCategory("Integration")] 
public void TestAlreadyPurchased() { ... }

Then we’ll update our continuous integration build definition to exclude any test with that category. If you are using Team Build in TFS, you’ll find this in the Process – Basic – Automated Tests – Test Assembly setting group of your build definition. In the Category Filter setting, enter the filter !Integration in order to exclude all unit tests that have the Integration TestCategory.

Here is an example Team Build screen shot of setting this Category Filter:

Builds - Automated Tests - Category Filter

You can then extend this concept to run different build definitions for all sorts of tests, and have your full integration tests only run when you want them to!

3 Comments

  1. Hi Jason,

    I am new to creating Integration Test cases, we are as of now do integration testing manually. By integration testing I mean interaction of different components involved in a solution.

    But actually, I want to do integration testing through ms test. Is this even possible? Am I even thinking in the right direction? I would be very happy if You can please shed down some light on it.

    Thanks
    Devesh

    1. Hi Devesh, you will see some suggestions in this blog post on how you can setup your integration unit tests to be excluded from your normal unit tests. In general, the majority of your component testing will be automated as regular unit tests within the component, and then you will have a smaller layer of automated integration tests above that.

      There are multiple ways to do that, but you can definitely just write normal unit tests in MSTest if your code can be invoked in that way. There are also other automation options for you, if you have other tools that are more familiar (Selenium, etc.)

      In general, your component integration unit test will just mimic the sequence the components are invoked in. It should then ensure that the full sequence achieves the expected outcome. They are just like normal unit tests in terms of functionality, but are testing beyond the normal scope of a unit test.

      Remember to limit the number of integration tests. The majority of your verifications should be running as unit tests inside the components, with the smaller set for integration purposes.

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 )

Connecting to %s