





















































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.)
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.
We are going to need some services to test, so we are going to use the PlacesToVisit application.
[TestFixture]
public class PlaceServiceTests
{
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();
}
[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.
[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>();
var startingCount = placeService
.Get(new AllPlacesToVisitRequest())
.Places
.Count;
var melbourne = new CreatePlaceToVisit
{
Name = "Melbourne",
Description = "A nice city to holiday"
};
placeService.Post(melbourne);
var newCount = placeService
.Get(new AllPlacesToVisitRequest())
.Places
.Count;
Assert.That(newCount == startingCount + 1);
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.
[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;
placeService.Put(canberra.ConvertTo<UpdatePlaceToVisit>());
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);
}
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.
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.
In this article, we covered some techniques to unit test ServiceStack applications.
Further resources on this subject: