Testing code that depends on a database can be slow and brittle.
This library aims to make it simple and fast to test your code that depends on a database.
Testcontainers are a great way to test your code that depends on a database. It can also be slow with the common xUnit pattern of using a "Test Collection" which means each test in that 'collection' is sequential. It's also annoying when creating lots of xUnit ceremony when making multiple collections. Finally, it's frustrating that multiple tests hitting the same Database can cause the tests to be brittle.
This library aims to make it simple to test your code that depends on a database:
- ✅ A single database per (xUnit) test class.
- ✅ Tests are now isolated from each other.
- ⚡ The entire test run can be faster because they now run in parallel (based off xUnit's Test Collections concept)
public class SomeTests(
SqlServerFixture SqlServerFixture, // The MSSql Testcontainer. Either this simple one or your own custom one.
ITestOutputHelper TestOutputHelper // xUnit's output helper.
)
{
private static readonly CancellationToken _cancellationToken = TestContext.Current.CancellationToken;
[Fact]
public async Task ToListAsync_GivenAnExistingDatabase_ShouldReturnAllUsers()
{
// Arrange.
// Grab our unique connection string.
var connectionString = SqlServerFixture.CreateDbConnectionString(TestContext.Current, TestOutputHelper);
using (var db = new SqlConnection(connectionString))
{
// Act.
var users = await db.QueryAsync<User>("SELECT * FROM Users", cancellationToken: _cancellationToken).ToListAsync(_cancellationToken);
}
// Assert.
....
}
public class SomeTests(
SqlServerFixture SqlServerFixture, // The MSSql Testcontainer. Either this simple one or your own custom one.
ITestOutputHelper TestOutputHelper // xUnit's output helper.
)
{
private static readonly CancellationToken _cancellationToken = TestContext.Current.CancellationToken;
[Fact]
public async Task ToListAsync_GivenAnExistingDatabase_ShouldReturnAllUsers()
{
// Arrange.
// Grab our unique connection string.
var connectionString = SqlServerFixture.CreateDbConnectionString(TestContext.Current, TestOutputHelper);
// ** We're using Entity Framework to connect to our unique Database instance.
// ** Can you anything you like here - like Dapper, etc.
// Wire up our EF Core DbContext to this unique connection string.
var efOptions = new DbContextOptionsBuilder<SqlServerDbContext>()
.UseSqlServer(connectionString)
.Options;
// Create our context.
var dbContext = new SqlServerDbContext(efOptions);
// Create the dabase if it doesn't exist (which it shouldn't)
await dbContext.Database.EnsureCreatedAsync(testContext.CancellationToken);
// ** DB is ready to go.
// Setup some fake data.
var user = new User
{
FirstName = "Princess",
LastName = "Leia",
Email = "[email protected]"
};
await dbContext.Users.AddAsync(user, _cancellationToken);
await dbContext.SaveChangesAsync(_cancellationToken);
// ** DB now has some data.
// Act.
var users = await dbContext.Users.ToListAsync(_cancellationToken);
// Assert.
Assert.NotNull(users);
}
}
Yep - contributions are always welcome. Please read the contribution guidelines first.
If you wish to participate in this repository then you need to abide by the code of conduct.
Yes! Please use the Issues section to provide feedback - either good or needs improvement 🆒