Transient database contexts for integration testing
TransientContext
is a simple tool for creating transient or disposable databases for integration
testing. During integration testing TransientContext
can be used to create a disposable database
from an existing schema and drop the database after tests are complete. TransientContext
supports
PostgreSQL and SQLite databases.
1.2.0
.NET Standard 2.0
The examples below are taken directly from TransientContext.Tests.NUnit
and TransientContext.Tests.Xunit
. In
the PostgreSQL examples connection strings are stored in user secrets files as described in the Microsoft documentation.
Begin by creating a database fixture. In the example below, DomainContext
represents
the EntityFramework
DBContext
that is being created. For reference, see TransientContext.Tests.NUnit/Common/Data/DomainContext.cs
.
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
var configuration = new ConfigurationBuilder()
.AddUserSecrets("your-secret-guid")
.Build();
TestDatabase = new TestDatabaseBuilder()
.WithConfiguration(configuration)
.Build();
/* Or for SQLite
*
* TestDatabase = new TestDatabaseBuilder()
* .WithConnectionString("Data Source=domain.db")
* .Build();
*/
TestDatabase.Create();
var builder = new DbContextOptionsBuilder<DomainContext>()
.UseNpgsql(TestDatabase.ConnectionString);
DbContext = new DomainContext(builder.Options);
DbContext.Database.EnsureCreated();
}
public ITestDatabase TestDatabase { get; }
public DomainContext DbContext { get; }
public void Dispose()
{
TestDatabase.Drop();
}
}
Add the DatabaseFixture
as part of the SetUp
and TearDown
methods in your test fixtures.
[TestFixture]
public class DomainContextTests
{
private DatabaseFixture _fixture;
private DomainContext _domainContext;
[SetUp]
public void Setup()
{
_fixture = new DatabaseFixture();
_domainContext = _fixture.DbContext;
}
[TearDown]
public void Teardown()
{
_fixture.Dispose();
}
private async Task<User> AddEntity()
{
var entity = await _domainContext.Users.AddAsync(new User
{
Username = "Alice"
});
await _domainContext.SaveChangesAsync();
return entity.Entity;
}
[Test]
public async Task should_add_entity()
{
var user = await AddEntity();
var count = await _domainContext.Users.CountAsync();
Assert.NotNull(user.Id);
Assert.AreEqual(1, count);
}
}
The examples below assume XUnit as the testing framework. These examples are taken directly
from TransientContext.Tests.Xunit
.
Begin by creating a database fixture. In the example below, DomainContext
represents
the EntityFramework
DBContext that is being created. For reference, see TransientContext.Tests.NUnit/Common/Data/DomainContext.cs
.
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
var configuration = new ConfigurationBuilder()
.AddUserSecrets("your-secret-guid")
.Build();
TestDatabase = new TestDatabaseBuilder()
.WithConfiguration(configuration)
.Build();
TestDatabase.Create();
var builder = new DbContextOptionsBuilder<DomainContext>();
builder.UseNpgsql(TestDatabase.ConnectionString);
DbContext = new DomainContext(builder.Options);
DbContext.Database.EnsureCreated();
}
public ITestDatabase TestDatabase { get; }
public DomainContext DbContext { get; }
public void Dispose()
{
TestDatabase.Drop();
}
}
[CollectionDefinition("Database")]
public class DatabaseCollectionFixture : ICollectionFixture<DatabaseFixture>
{
}
Add the DatabaseFixture
as part of a test class constructor.
[Collection("Database")]
public class UserTests
{
private readonly TestDbContext _testDbContext;
public UserTests(DatabaseFixture databaseFixture)
{
_testDbContext = databaseFixture.DbContext;
}
[Fact]
public async Task InsertUsers()
{
await _testDbContext.Users.AddAsync(new User {Username = "Alice"});
await _testDbContext.Users.AddAsync(new User {Username = "Jane"});
await _testDbContext.SaveChangesAsync();
var count = await _testDbContext.Users.CountAsync();
Assert.Equal(2, count);
}
}