Anthony-Nolan / Atlas

A free & open-source Donor Search Algorithm Service

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 Tests 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.