Scenario
I have an API on ASP.NET Core 2.0 that integrates with MS SQL database using EF Core. Now I am trying to setup integration/api tests for it using NUnit and TestServer
. The issue is that I need to configure every test to be 'isolated' so basically it should clean up (rollback) DB after self. I cannot use compensation transactions to achieve the desired result due to the complexity of DB (lots of legacy stuff to account for, e.g. triggers etc).
SUT API Setup
Here is an example of API I am trying to test:
// GET api/values
[HttpGet]
public async Task<IActionResult> Get(string dataName1, string dataName2)
{
using (var scope = CreateScope())
{
await _service.DoWork(dataName1);
await _service.DoWork(dataName2);
scope.Complete();
}
return Ok();
}
The DoWork()
method basically looks up an entity given passed parameter and increments its another property. Then simply calls SaveChanges()
.
The CreateScope()
is here a helper method that returns an instance of TransactionScope
:
return new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.RepeatableRead },
TransactionScopeAsyncFlowOption.Enabled
);
Integration Test Setup
[TestFixture]
[SingleThreaded]
[NonParallelizable]
public class TestTest
{
private TransactionScope _scope;
[Test]
public async Task Test11()
{
_scope = CreateScope();
var result = await Client.GetAsync("api/values?dataName1=Name1&dataName2=Name2");
Assert.DoesNotThrow(() => result.EnsureSuccessStatusCode());
_scope.Dispose();
_scope = null;
}
}
Here the Client
is an instance of HttpClient
created by utilizing Microsoft.AspNetCore.TestHost.TestServer
and CreateScope()
method is actually the same as in the API.
This simple case works fine - the changes made by my SUT API are rollbacked successfully by calling _scope.Dispose()
and DB returns to its 'clean' state.
Problem
Now I want to move the logic related to create/rollback of scope outside of my test method and put it in SetUp/TearDown so all my tests are automatically handled.
[SetUp]
public async Task SetupTest()
{
_scope = TransactionHelper.CreateScope();
}
[TearDown]
public async Task TeardownTest()
{
_scope.Dispose();
_scope = null;
}
[Test]
public async Task Test11()
{
var result = await OneTimeTestFixtureStartup.Client.GetAsync("api/values?dataName1=Name1&dataName2=Name2");
Assert.DoesNotThrow(() => result.EnsureSuccessStatusCode());
}
But it does NOT work (I can see modifications in DB after test run) for some reason and I cannot figure it out.
Why? What am I missing?
Note: both tests versions pass successfully.