The tests in ServiceStack.WebHost.Endpoints.Tests show good examples of how to create stand-alone integration tests that just use a self-hosted HttpListener AppHost.
Quick Mix
Use mix to quickly create a .NET 6 Integration Test project:
$ x mix init-test
Or if you need to create a source compatible .NET 6 and .NET v4.72 Integration Test project:
$ x mix init-test2
Example Stand-alone Integration tests
Integration Testing
If your test project is targeting netcore, AppSelfHostBase
is available in the ServiceStack.Kestrel
package.
The CustomerRestExample.cs shows an example of a stand-alone integration test. Integration tests in ServiceStack just involves starting a standard self-host ServiceStack Instance when the Test Fixture Starts up and disposing it when it tears down. Your integration tests can then communicate with the self-host exactly the same as if it were a remote ServiceStack instance (since that's all it is), e.g:
//Create your ServiceStack AppHost with only the dependencies it needs
public class AppHost : AppSelfHostBase
{
public AppHost() : base("Customer REST Example", typeof(CustomerService).Assembly) {}
public override void Configure(Container container)
{
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider));
using var db = container.Resolve<IDbConnectionFactory>().Open();
db.CreateTableIfNotExists<Customer>();
}
}
//Add Service Contract DTO's and Data Models
[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> {}
public class GetCustomersResponse
{
public List<Customer> Results { get; set; }
}
[Route("/customers", "POST")]
public class CreateCustomer : IReturn<Customer>
{
public string Name { get; set; }
}
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
//Implement your Service Contracts
public class CustomerService : Service
{
public object Get(GetCustomers request)
{
return new GetCustomersResponse { Results = Db.Select<Customer>() };
}
public object Post(CreateCustomer request)
{
var customer = new Customer { Name = request.Name };
Db.Save(customer);
return customer;
}
}
//Write your Integration tests
public class CustomerRestExample
{
const string BaseUri = "http://localhost:2000/";
ServiceStackHost appHost;
public CustomerRestExample()
{
//Start your AppHost on OneTimeSetUp
appHost = new AppHost()
.Init()
.Start(BaseUri);
}
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
/* Write your Integration Tests against the self-host instance */
[Test]
public void Can_GET_and_Create_Customers()
{
var client = new JsonServiceClient(BaseUri);
//GET /customers
var all = client.Get(new GetCustomers());
Assert.That(all.Results.Count, Is.EqualTo(0));
//POST /customers
var customer = client.Post(new CreateCustomer { Name = "Foo" });
Assert.That(customer.Id, Is.EqualTo(1));
//GET /customers
all = client.Get(new GetCustomers());
Assert.That(all.Results.Count, Is.EqualTo(1));
}
}
Unit testing
If you want to unit test a ServiceStack Service in isolation there are a couple of different approaches you can take. The base Service class itself is just a simple C# class which lets you define and inject dependencies manually or by using the built-in IOC container.
We'll illustrate both approaches using this simple unit test example that tests this simple Service:
// DTOs
public class FindRockstars
{
public int? Aged { get; set; }
public bool? Alive { get; set; }
}
public class GetStatus
{
public string LastName { get; set; }
}
public class RockstarStatus
{
public int Age { get; set; }
public bool Alive { get; set; }
}
public class Rockstar
{
[AutoIncrement]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int? Age { get; set; }
}
// Implementation
public class SimpleService : Service
{
public IRockstarRepository RockstarRepository { get; set; }
public List<Rockstar> Get(FindRockstars request)
{
return request.Aged.HasValue
? Db.Select<Rockstar>(q => q.Age == request.Aged.Value)
: Db.Select<Rockstar>();
}
public RockstarStatus Get(GetStatus request)
{
var rockstar = RockstarRepository.GetByLastName(request.LastName);
if (rockstar == null)
throw HttpError.NotFound($"'{request.LastName}' is not a Rockstar");
var status = new RockstarStatus
{
Alive = RockstarRepository.IsAlive(request.LastName)
}.PopulateWith(rockstar); //Populates with matching fields
return status;
}
}
This Service provides 2 operations, FindRockstars
which makes db queries directly in the service class itself, and GetStatus
which uses a repository instead for all its Data access.
Using an in-memory database
If you're accessing Db
from directly within your service implementation you're going to want to make use of a real DB given the ADO.NET IDbConnection requires a lot of effort to mock. You can do this in the same way you would register your dependencies in ServiceStack itself, by using the built-in IOC. For a unit test we can do this without an AppHost by just use a new Container
in your OneTimeSetUp
, e.g:
Test Setup
private ServiceStackHost appHost;
[OneTimeSetUp]
public void OneTimeSetUp()
{
appHost = new BasicAppHost().Init();
var container = appHost.Container;
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider));
container.RegisterAutoWiredAs<RockstarRepository, IRockstarRepository>();
container.RegisterAutoWired<SimpleService>();
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
db.DropAndCreateTable<Rockstar>();
db.InsertAll(SeedData);
}
}
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
With everything setup we can now test the service just like a normal C# class in isolation independently of ServiceStack itself:
[Test]
public void Using_in_memory_database()
{
//Resolve the autowired service from IOC and set Resolver for the base class
var service = appHost.Container.Resolve<SimpleService>();
var rockstars = service.Get(new FindRockstars { Aged = 27 });
rockstars.PrintDump(); //Print a dump of the results to Console
Assert.That(rockstars.Count, Is.EqualTo(SeedData.Count(x => x.Age == 27)));
var status = service.Get(new GetStatus { LastName = "Vedder" });
Assert.That(status.Age, Is.EqualTo(48));
Assert.That(status.Alive, Is.True);
status = service.Get(new GetStatus { LastName = "Hendrix" });
Assert.That(status.Age, Is.EqualTo(27));
Assert.That(status.Alive, Is.False);
Assert.Throws<HttpError>(() =>
service.Get(new GetStatus { LastName = "Unknown" }));
}
Manually injecting dependencies
If you prefer your unit tests not to use an in-memory database, you can instead choose to mock your dependencies. In this example we'll use a stand-alone Mock, but you can reduce boilerplate by using mocking library like Moq instead.
public class RockstarRepositoryMock : IRockstarRepository
{
public Rockstar GetByLastName(string lastName)
{
return lastName == "Vedder"
? new Rockstar(6, "Eddie", "Vedder", 48)
: null;
}
public bool IsAlive(string lastName)
{
return lastName == "Grohl" || lastName == "Vedder";
}
}
[Test]
public void Using_manual_dependency_injection()
{
var service = new SimpleService
{
RockstarRepository = new RockstarRepositoryMock()
};
var status = service.Get(new GetStatus { LastName = "Vedder" });
Assert.That(status.Age, Is.EqualTo(48));
Assert.That(status.Alive, Is.True);
Assert.Throws<HttpError>(() =>
service.Get(new GetStatus { LastName = "Hendrix" }));
}
This example doesn't need a container as we're injecting all the dependencies manually.
Testing ServiceStack classes in Unit Tests
Much of ServiceStack functionality assumes there's an AppHost is available which for Unit Tests you can just use an In Memory AppHost, e.g:
[Test]
public void My_unit_test()
{
using (new BasicAppHost().Init())
{
//test ServiceStack classes
}
}
If preferred this can be set up once per test fixture following this pattern:
public class MyUnitTests
{
ServiceStackHost appHost;
public MyUnitTests() => appHost = new BasicAppHost().Init();
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
[Test]
public void My_unit_test()
{
//test ServiceStack classes
}
}
Community Resources
- ServiceStack 4 HTTP Utilities: Contract Testing by @kylehodgson
- Regression testing ServiceStack services with RavenDB embedded by @wayne_douglas
- ServiceStack – Testing services with Chrome REST Console
- ServiceStack and RavenDB End to End Testing by @aquabirdconsult
- Integration Testing With ServiceStack by @rossipedia
- How to unit test your database code when using ServiceStack OrmLite by @rickardn
- EasyHttp and ServiceStack, making the mspec tests better by @chrissie1
- Using ServiceStack for the EasyHttp integration tests by @chrissie1
- Parameterized Tests for ServiceStack Web Services