Integration Tests share dependencies between test features
benbelow opened this issue · comments
A standard Integration TestFixture
has a SetUp
method like this:
[SetUp]
public void SetUp()
{
var dbProvider = DependencyInjection.DependencyInjection.Provider.GetService<IActiveDatabaseProvider>();
activeDb = dbProvider.GetActiveDatabase();
var activeDbConnectionStringProvider = DependencyInjection.DependencyInjection.Provider.GetService<ActiveTransientSqlConnectionStringProvider>();donorInspectionRepository = new TestDonorInspectionRepository(activeDbConnectionStringProvider);
managementService = DependencyInjection.DependencyInjection.Provider.GetService<IDonorManagementService>();
DatabaseManager.ClearDatabases();
}
It it natural to assume that this means we'll get a new, say, managementService
object for each test.In fact the ServiceProvider
is providing the same managementService
object every time (and likewise the same dependency objects), leading to potentially subtle bugs, and making tests not necessarily independent of one another, both within TestFixtures and also across separate TestFixtures
The fix is to use a service provision Scope
, but it’s critical that you don’t dispose that scope in the SetUp method, but rather in the TearDown method.
#region Dependency Scoping.
private IServiceScope perTestDependencyScope;
[SetUp]
public void SetUp()
{
//We need each test to be run with new objects. Which means a new DependencyInjection scope for each test :(
perTestDependencyScope = DependencyInjection.DependencyInjection.Provider.CreateScope();
var activeDbConnectionStringProvider = perTestDependencyScope.ServiceProvider.GetService<ActiveTransientSqlConnectionStringProvider>();
donorInspectionRepository = new TestDonorInspectionRepository(activeDbConnectionStringProvider);
managementService = perTestDependencyScope.ServiceProvider.GetService<IDonorManagementService>();
DatabaseManager.ClearDatabases();
}
[TearDown]
public void DisposeDependencyScope()
{
// If we use a `using` block, then we dispose everything we made in that block, which means we dispose the contexts :(
// Which then error when we use them in the tests.
perTestDependencyScope.Dispose();
}
#endregion
You may then also want to move some of these object resolutions into [OneTimeSetUp]
tests, if they genuinely can be shared across all Test
s in a TestFixture
, though note you'll need to think carefully about scopes in that case, to ensure that shared sub-objects are managed properly.