Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

ServiceStack applications

Save for later
  • 9 min read
  • 21 Jan 2015

article-image

In this article by Kyle Hodgson and Darren Reid, authors of the book ServiceStack 4 Cookbook, we'll learn about unit testing ServiceStack applications.

(For more resources related to this topic, see here.)

Unit testing ServiceStack applications

In this recipe, we'll focus on simple techniques to test individual units of code within a ServiceStack application. We will use the ServiceStack testing helper BasicAppHost as an application container, as it provides us with some useful helpers to inject a test double for our database. Our goal is small; fast tests that test one unit of code within our application.

Getting ready

We are going to need some services to test, so we are going to use the PlacesToVisit application.

How to do it…

  1. Create a new testing project. It's a common convention to name the testing project <ProjectName>.Tests—so in our case, we'll call it PlacesToVisit.Tests. Create a class within this project to contain the tests we'll write—let's name it PlacesServiceTests as the tests within it will focus on the PlacesService class. Annotate this class with the [TestFixture] attribute, as follows:
    [TestFixture]
    public class PlaceServiceTests
    {
  2. We'll want one method that runs whenever this set of tests begins to set up the environment and another one that runs afterwards to tear the environment down. These will be annotated with the NUnit attributes of TestFixtureSetUp and TextFixtureTearDown, respectively. Let's name them FixtureInit and FixtureTearDown.

    In the FixtureInit method, we will use BasicAppHost to initialize our appHost test container. We'll make it a field so that we can easily access it in each test, as follows:

    ServiceStackHost appHost;

    [TestFixtureSetUp]
    public void FixtureInit()
    {
    appHost = new BasicAppHost(typeof(PlaceService).Assembly)
    {
       ConfigureContainer = container =>
       {
         container.Register<IDbConnectionFactory>(c =>
           new OrmLiteConnectionFactory(
             ":memory:", SqliteDialect.Provider));
         container.RegisterAutoWiredAs<PlacesToVisitRepository,
           IPlacesToVisitRepository>();
       }
    }.Init();
    }

    The ConfigureContainer property on BasicAppHost allows us to pass in a function that we want AppHost to run inside of the Configure method. In this case, you can see that we're registering OrmLiteConnectionFactory with an in-memory SQLite instance. This allows us to test code that uses a database without that database actually running. This useful technique could be considered a classic unit testing approach—the mockist approach might have been to mock the database instead.

    The FixtureTearDown method will dispose of appHost as you might imagine. This is how the code will look:

    [TestFixtureTearDown]
    public void FixtureTearDown()
    {
    appHost.Dispose();
    }
  3. We haven't created any data in our in memory database yet. We'll want to ensure the data is the same prior to each test, so our TestInit method is a good place to do that—it will be run once before each and every test run as we'll annotate it with the [SetUp] attribute, as follows:
    [SetUp]
    public void TestInit()
    {
    using (var db = appHost.Container
         .Resolve<IDbConnectionFactory>().Open())
    {
       db.DropAndCreateTable<Place>();
       db.InsertAll(PlaceSeedData.GetSeedPlaces());
    }
    }

    As our tests all focus on PlaceService, we'll make sure to create Place data.

  4. Next, we'll begin writing tests. Let's start with one that asserts that we can create new places. The first step is to create the new method, name it appropriately, and annotate it with the [Test] attribute, as follows:
    [Test]
    public void ShouldAddNewPlaces()
    {

    Next, we'll create an instance of PlaceService that we can test against. We'll use the Funq IoC TryResolve method for this:

    var placeService = appHost.TryResolve<PlaceService>();
  5. We'll want to create a new place, then query the database later to see whether the new one was added. So, it's useful to start by getting a count of how many places there are based on just the seed data. Here's how you can get the count based on the seed data:
    var startingCount = placeService
                   .Get(new AllPlacesToVisitRequest())
                   .Places
                   .Count;
  6. Since we're testing the ability to handle a CreatePlaceToVisit request, we'll need a test object that we can send the service to. Let's create one and then go ahead and post it:
    var melbourne = new CreatePlaceToVisit
    {
       Name = "Melbourne",
       Description = "A nice city to holiday"
    };
    placeService.Post(melbourne);
  7. Having done that, we can get the updated count and then assert that there is one more item in the database than there were before:
    var newCount = placeService
                   .Get(new AllPlacesToVisitRequest())
                   .Places
                  .Count;
    Assert.That(newCount == startingCount + 1);
  8. Next, let's fetch the new record that was created and make an assertion that it's the one we want:
    var newPlace = placeService.Get(new PlaceToVisitRequest
    {
       Id = startingCount + 1
    });
    Assert.That(newPlace.Place.Name == melbourne.Name);
    }

    With this in place, if we run the test, we'll expect it to pass both assertions. This proves that we can add new places via PlaceService registered with Funq, and that when we do that we can go and retrieve them later as expected.

    Unlock access to the largest independent learning library in Tech for FREE!
    Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
    Renews at AU $19.99/month. Cancel anytime
  9. We can also build a similar test that asserts that on our ability to update an existing place. Adding the code is simple, following the pattern we set out previously. We'll start with the arrange section of the test, creating the variables and objects we'll need:
    [Test]
    public void ShouldUpdateExistingPlaces()
    {
    var placeService = appHost.TryResolve<PlaceService>();
    var startingPlaces = placeService
         .Get(new AllPlacesToVisitRequest())
         .Places;
    var startingCount = startingPlaces.Count;
     

    var canberra = startingPlaces
         .First(c => c.Name.Equals("Canberra"));

    const string canberrasNewName = "Canberra, ACT";
    canberra.Name = canberrasNewName;
  10. Once they're in place, we'll act. In this case, the Put method on placeService has the responsibility for update operations:

    placeService.Put(canberra.ConvertTo<UpdatePlaceToVisit>());

  11. Think of the ConvertTo helper method from ServiceStack as an auto-mapper, which converts our Place object for us. Now that we've updated the record for Canberra, we'll proceed to the assert section of the test, as follows:
    var updatedPlaces = placeService
         .Get(new AllPlacesToVisitRequest())
         .Places;
    var updatedCanberra = updatedPlaces
         .First(p => p.Id.Equals(canberra.Id));
    var updatedCount = updatedPlaces.Count;

    Assert.That(updatedCanberra.Name == canberrasNewName);
    Assert.That(updatedCount == startingCount);
    }

How it works…

These unit tests are using a few different patterns that help us write concise tests, including the development of our own test helpers, and with helpers from the ServiceStack.Testing namespace, for instance BasicAppHost allows us to set up an application host instance without actually hosting a web service. It also lets us provide a custom ConfigureContainer action to mock any of our dependencies for our services and seed our testing data, as follows:

appHost = new BasicAppHost(typeof(PlaceService).Assembly)
{
ConfigureContainer = container =>
{
   container.Register<IDbConnectionFactory>(c =>
     new OrmLiteConnectionFactory(     ":memory:", SqliteDialect.Provider));

   container.RegisterAutoWiredAs<PlacesToVisitRepository,     IPlacesToVisitRepository>();
}
}.Init();

To test any ServiceStack service, you can resolve it through the application host via TryResolve<ServiceType>().This will have the IoC container instantiate an object of the type requested. This gives us the ability to test the Get method independent of other aspects of our web service, such as validation. This is shown in the following code:

var placeService = appHost.TryResolve<PlaceService>();

In this example, we are using an in-memory SQLite instance to mock our use of OrmLite for data access, which IPlacesToVisitRepository will also use as well as seeding our test data in our ConfigureContainer hook of BasicAppHost. The use of both in-memory SQLite and BasicAppHost provide fast unit tests to very quickly iterate our application services while ensuring we are not breaking any functionality specifically associated with this component. In the example provided, we are running three tests in less than 100 milliseconds. If you are using the full version of Visual Studio, extensions such as NCrunch can allow you to regularly run your unit tests while you make changes to your code. The performance of ServiceStack components and the use of these extensions results in a smooth developer experience with productivity and quality of code.

There's more…

In the examples in this article, we wrote out tests that would pass, ran them, and saw that they passed (no surprise). While this makes explaining things a bit simpler, it's not really a best practice. You generally want to make sure your tests fail when presented with wrong data at some point. The authors have seen many cases where subtle bugs in test code were causing a test to pass that should not have passed. One best practice is to write tests so that they fail first and then make them pass—this guarantees that the test can actually detect the defect you're guarding against. This is commonly referred to as the red/green/refactor pattern.

Summary

In this article, we covered some techniques to unit test ServiceStack applications.

Resources for Article:


Further resources on this subject: