How do I wrap integration tests that use TestServer in a database transaction?

entity-framework-core xunit

Question

Using xUnit and the TestServer from Microsoft.AspNet.TestHost, how can I wrap each test in a database transaction that can be rolled back after the test?

Here's how I create the TestServer:

TestServer = new TestServer(TestServer.CreateBuilder()
    .UseStartup<Startup>());

The Startup that's referenced there is the Startup from the web app project. In the ConfigureServices method in that Startup class I add EF like this:

services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<TrailsDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

I could pull the DbContext back of services and store a static reference on the Startup class, but that seems pretty hacky. Is there any way I can instantiate the DbContext where I create the TestServer and somehow have the web app use that instead of the one in the Startup class?

Edit: I have tried instantiating another instance of the DbContext where I create the TestServer and using that context to delete and recreate the database before each test, but that adds about 10 seconds to each test's run time.

1
3
3/1/2016 12:08:18 AM

Popular Answer

Some advice: the simplest approach would be to destroy the test database at the end and recreate for each test run. This ensures no lingering test-to-test contamination.

But since you asked how, this can be done by extending Xunit. Xunit allows you to define custom test cases and test runners. A complete answer is hard to include in a SO answer. The simplest solution uses ambient transactions. (Danger! Ambient transactions can be tricky.) Xunit has a sample for a custom BeforeAfterTestAttribute that rolls back a transaction. https://github.com/xunit/samples.xunit/tree/master/AutoRollbackExample. To use ambient transactions, turn off the default EF setting that throws if ambient transactions are present.(optionsBuilder.UseSqlServer().SuppressAmbientTransactionWarning()).

A more complicated, but better solution is to override XunitTestCaseRunner and inject a transaction into each test case, ensuring to rollback at the conclusion of each test.

Also, EF docs provides a sample of using the InMemory provider to test. You may find this useful. "Testing In Memory : EF Core Docs"

2
3/1/2016 12:20:55 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow