I love SS but I'm scratching my head trying to unit test my business layer. I'm new to unit testing andmocking and been reading up on NSubstitute as this looks like a fun mocking layer.
I have my file structure roughly like this:
MainAppHostProject*
|
-AppStart
-AppHost <-- standard apphost
DtoProject*
|
-HelloWorldDto <-- simple POCO to
ServiceLayerProject*
|
-HelloWorldService <-- service interface that merely passes/sends Dtos to/from business layer
BusinessLayerProject*
|
-HelloWorldManager <-- logic to construct response and this class extends 'Service' (letting me access Db, session, etc)...sidenote: maybe i shouldve called this a HelloWorldRepository?
-CustomAuthProvider
-CustomUserSession
DaoProject*
|
-HelloWorldDao <-- POCO of table structure
The Apphost points to the HelloWorldService assembly and registers the SQL Server database as standard.
Everything actually works great and I have been able to build up the logic in a cleaner way. Unfortunately I wish to embark on unit testing BUT I dont know how to decouple the database.
I tried to register a fake in memory database but then I think there's incompatibility issues with how I've used code to get identities etc in SQL Server vs SQLite ways.
// container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(":memory:", false, SqliteOrmLiteDialectProvider.Instance));
// container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(":memory:", false, SqlServerDialect.Provider));
I just want to decouple and unit test. Any ideas please?
***UPDATE
public class UnitTest1
{
private Container container;
[TestMethod]
public void TestMethod1()
{
container = new Container();
// container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(":memory:", false, SqliteDialect.Provider));
// sqlite didnt work so attempting with a real DB for now
var connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=XXX;Integrated Security=True";
container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
// dependencies are injecting ok
container.RegisterAutoWiredAs<FeedbackRepo, IFeedbackRepo>();
// service is autowiring --> leading to good injections
container.RegisterAutoWired<FeedbackService>();
var service = container.Resolve<FeedbackService>();
service.SetResolver(new BasicResolver(container));
// unit test is working well
var request = new DTO.FeedbackDto { Message = "test" };
bool result = service.Post(request);
}
}
At the moment trying to get 'Db' to stop being null in my derived Service classes.
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
Implementation
This Service provides 2 operations,
FindRockstars
which makes db queries directly in the service class itself, andGetStatus
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 newContainer
in yourTestFixtureSetup
, e.g:Test Setup
With everything setup we can now test the service just like a normal C# class in isolation independently of ServiceStack itself:
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.
This example doesn't need a container as we're injecting all the dependencies manually. I've also added this example to the Testing wiki docs.