¿Probando WebAPI con un cliente dentro de unscope de transacción?

Pregunta

Is there any way to specify a connection for the transactionscope so that the effects of this "out-of-context" test can be rolled back?

Antecedentes

I'm using RestSharp to test WebAPI controllers. This is an integration test and tests by sending a request to the API, which propagates all the way down to the data layer through the repository and service layers. In other words, this is intentionally NOT a unit test.

The test creates an instance of restClient:

    [TestInitialize()]
    public virtual void TestInitialize()
    {

        _client = new RestClient("http://localhost:24144");
        _client.CookieContainer = new System.Net.CookieContainer();

    }

The client then makes some requests, totally ignorant of the data context (again, intentional):

 // pseudocode
 _client.DoThis();
 _client.DoThat();

This test is run against a test database which is generated from EF code-first migrations. I have an empty seed method so the database is empty:

    protected override void Seed(TestAppDbContext context)
    {
        SqlConnection.ClearAllPools();
        context.Database.Delete();
        context.Database.CreateIfNotExists();
    }

El problema

This is just one of many tests that I would like to run. Currently I have to drop/recreate the database between tests AND make sure that the Api Test gets run after the other tests. Since it doesn't know anything about the data context, I can't (or haven't been able to figure out how to) wrap all of the test code in a transaction to roll back.

Lo que he probado

  • newing up a transactionscope in TestInitialize and disposing it in TestCleanup. This does nothing.
  • running 'update-database' after this test just for kicks - but I always get 'Cannot drop database because it is currently in use' despite my call to ClearAllPools(). This isn't the issue that I'd like to focus on but thought it was worth mentioning.

preguntado el 05 de mayo de 13 a las 21:05

I too am currently struggling with this. Originally I was copying a fresh, empty database file and running migration/seeding before each test. This takes some time, so I wanted to optimize by doing this once per test class instead of per test, and using TransactionScope to roll back changes made per test. However, I get a deadlock situation on my self hosted WebApi when it tries to access the database. If I comment out the transaction scope in the test code, it works, but the changes are not rolled back. I tried setting IsolationLevel to ReadCommitted, but that didn't help. -

2 Respuestas

I hit a similar issue a few months ago, but didn't come up with a good solution using TransactionScope for many of the same reasons you state.

he finalizado De Web Hosting WebAPI in the test project. And using a SQLCE 4 database that can be deleted and recreated (and migrated) before each test run. This has been up and running now for a while and works really well.

contestado el 05 de mayo de 13 a las 22:05

+1, you have actually accidentally answered a question I was recently pondering myself :) Where did you create the SQLLite database for the test purposes, out of interest? - Moo-Jugo

@Moo-Juice SQLCE 4, like SqlLite is a file database, so I just let it create the database in the default location. In this case it was the bin directory. - Davin Tryon

Pude conseguir algo of my integration tests running with IsolationLevel.RepeatableRead.

However, tests that involved updating entities via the API did not succeed. Still looking for the perfect solution.

Copying in a new database file per test as in David's answer is the only fully working solution so far, but the performance leaves much to be desired. Especially if the database schema is out of date and must be migrated/seeded for each test.

Example code in NUnit:

[SetUp]
public void TestSetUp()
{
    _transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() {IsolationLevel = IsolationLevel.RepeatableRead, Timeout = TimeSpan.FromSeconds(5)});
}

[TearDown]
public void TestTearDown()
{
    // Roll back any changes made, per test.
    _transactionScope.Dispose();
}

contestado el 20 de mayo de 13 a las 18:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.