2

I am (like many others) running into the problem that I can't get my NUnit tests to delete the database in the [SetUp] of my test fixture.

What I want to achieve

I want to write integration tests that test my code and verify if the expected results are stored in the database (Assert CRUD methods). That, and I want to be able to actually show the tables in SqlServer and see how the results in the database look like. That last part seems a hard thing to achieve...

What is going wrong

I can run my tests, many times in a row. The database is recreated in the [SetUp] every time and the tests pass the asserts. It goes down the drain as soon as I want to check my result in the actual database in SqlServer. Once I opened a connection from SqlServer, the [SetUp] method is not allowed to delete the database, since it has open connections.

What have I tried

  • Database initializer
  • ALTER database SET SINGLE_USER WITH ROLLBACK IMMEDIATE
  • Pooling=false added to connection string

I took those ideas from this and this SO post.

What do I have

The [SetUp] method:

    [SetUp]
    public void SetUp()
    {
        // TenantSeedInitializer extends the 
        // DropCreateDatabaseAlways<TenantApplicationTestContext> class

        Database.SetInitializer(new TenantSeedInitializer());
        _applicationContext = new TenantApplicationTestContext();
        _applicationContext.Database.ExecuteSqlCommand("ALTER DATABASE " + 
            TenantApplicationTestContext.DatabaseName + 
            " SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
    }

The [TearDown] method:

    [TearDown]
    public void TearDown()
    {
        SqlConnection.ClearAllPools();
    }

And a pretty straight forward test:

    [Test]
    public void AddTenant()
    {
        // add a new tenant to the database and verify that there
        // there is only one tenant present in the table  
    }

As I said, running this test several times in a row works like a charm, until the point where I try to open the table in SqlServer.

Two different (for me unsolved) error scenario's

1) Either I am not allowed to view the table because the connection from visual studio is still open.

Database 'TestTenantDatabase' is already open and can only have one user at a time.

Adding the SqlConnection.ClearAllPools(); does not seem to solve this.

2) Or I am allowed to view the table in SqlServer, and then I am no longer allowed to delete the database from my [SetUp] fixture.

Cannot drop database "TestTenantDatabase" because it is currently in use.

Shutting down SqlServer is the only way I know to get rid of this. But then I find myself restarting SqlServer a lot during the day... (an option to close the connection to the database would also help, but I can't find it).

Can anybody guide me through this?

Community
  • 1
  • 1
bas
  • 13,550
  • 20
  • 69
  • 146
  • Can you kill the offending connection? – thebjorn Feb 24 '13 at 11:16
  • I can close visual studio or sqlserver but that's pretty time consuming. Other solutions are unknown to me – bas Feb 24 '13 at 11:33
  • 1
    use `sp_who` to list all connections, then `kill ` to kill one of them. – thebjorn Feb 24 '13 at 11:38
  • @thebjorn, I removed my previous comment which stated I couldn't get it to work. I **can** get it to work with kill id. So that's a starting point. At least less time consuming. I'll pay some more attention to the answer below now. Maybe I can find a way that allows me to do less manual work. thx for you comment tho! – bas Feb 24 '13 at 12:41

1 Answers1

0

This message:

Database 'TestTenantDatabase' is already open and can only have one user at a time.

is happening because you have set the database to single user. Usually you would do this kill other connections to the database, and make your connection the only one that can access it. However, if you change your database context some other application could connect in, like Visual Studio, and become the single user, effectively locking you out.

Try this in your setup fixture:

use TestTenantDatabase;
alter database TestTenantDatabase set single_user with rollback immediate;
use master;
drop database TestTenantDatabase;

If you are in the database when you set single user, you will become the single user. Then, if you change to master and then delete the database within the same batch, it should beat anyone trying to connect into it.

Another option is to set offline, instead of single_user, however, when you then delete the database, it will not delete the database .mdf & .ldf (and any .ndf) files, and so you could have issues recreating the database for another set of tests.

If you get errors relating to deadlocks when running single_user, set your deadlock priority to high. You can set it back to normal after the drop, if you are going to reuse the connection.

muhmud
  • 4,474
  • 2
  • 15
  • 22
  • Nah no luck with that either. Gives the exact same message 'Cannot drop database "TestTenantDatabase" because it is currently in use.'. Plus I am trying to use `DropCreateDatabaseAlways` to remove the database so that I can stay away of handwritten sql code in my business code and test code. The only sort of work around I got so far is killing the processes and start the test again. – bas Feb 24 '13 at 12:50
  • You could offline then instead of single_user, drop the database, and then handle the database files yourself. – muhmud Feb 24 '13 at 13:38
  • Yeah I thought you'd say that :). Meh. Was hoping that there would be some more friendly way if doing this – bas Feb 24 '13 at 13:58
  • OK, if you know which login is being used, you could disable the login, then do the steps in the answer, then re-enable the login. The login must be constantly trying to connect, hence it's managing to get in before you can drop. To disable a login do `alter login [] disable`. Do `enable` to enable it again. You're going to need server level permissions in order to run these statements. – muhmud Feb 24 '13 at 14:56