Sagittaras.Model.TestFramework by Sagittaras Games
Library for creating xUnit tests of Database Layer.
- Services factory for tests
- Support for parallelism
- Empty database per test case
- Scoped instances of services per test case
- Database clean-up after the test
For the support of parallelism and clean database for every test case, each database should be initialized with unique name. This is done through
GetConnectionString(engine)
method, which prepares the connection string with uniquely generated database name.
How the unique connection string is generated is declared by the parameter and enum Engine
.
When we are testing a data model layer we also need a access to the database itself. To ensure unique database credentials through environments the Test Framework is using User Secrets.
Everything needed to do is just initialize a secret storage in the test project and add custom connection (without database name).
dotnet user-secrets set "ConnectionStrings:UnitTest" "Server=localhost;Port=5432;User Id=sa;Password=SuperSecretPassword"
The framework will automatically adds a ;Database=<name>
to the connection string with uniquely generated name.
User used in database connection string should have rights to create and drop databases
When using InMemory database the connection string in user secrets is not required. Framework only generates a unique data source name.
Test Factory is a class providing options to configure a service provider used inside of tests. It is needed to create a new
class extending from TestFactory
and overrides OnConfiguring
method.
public class UnitTestFactory : TestFactory
{
/// <summary>
/// By default the connection string name is defined as UnitTest. But we are able
/// to change this name simply by overriding the ConnectionString property.
/// </summary>
protected override string ConnectionString => "Postgre";
/// <summary>
/// We define our database context with generated connection string and
/// add any required service. Or modify the available ones.
/// </summary>
protected override void OnConfiguring(ServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => {
options.UseSqlServer(GetConnectionString(Engine.DbEngine)); // Or any other database engine
});
services.AddScoped<IUserService, UserService>();
}
}
For every test class, the UnitTest
is base class with generic parameters pointing at our
test factory and used instance of Database Context.
This class uses xUnit's IAsyncLifetime
allowing asynchronous creating and deletion of used database.
Provided properties:
TFactory Factory { get; }
Generic property pointing atTestFactory
classITestOutputHelper TestOutputHelper { get; }
xUnit's class writing outputs to test resultsIServiceProvider ServiceProvider { get; }
Instance of scoped service provider for the current testTDbContext Context { get; }
Generic property with access to database context class
/// <summary>
/// We can extend a new class to create custom basic class for the Unit test. Allowing
/// multiple different configurations for the Unit tests.
/// </summary>
public abstract class MyUnitTest : UnitTest<UnitTestFactory, MyDbContext>
{
protected MyUnitTest(UnitTestFactory factory, ITestOutputHelper testOutputHelper) : base(factory, testOutputHelper)
{
}
}