4

I have a code that uses EF code first that I want to Unit tests in my unit test I want a real empty database in the start of the test so I did:

    [TestInitialize]
    public void Initialize()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

    }

    [TestCleanup]
    public void CleanUp()
    {

        MyContext db = new MyContext();
        db.Database.Delete();
    }

but because the tests run in parallel this is not work so I did a order test with my tests and it also has issues because the database sometimes is not dropped because is in use... Someone have better strategy? I thought maybe each test will create its own database ? if it is good idea how can I achieve this?

ilay zeidman
  • 2,654
  • 5
  • 23
  • 45
  • From the moment you're using a real database, and if you are testing the database access, I think that this code is not a unit test, it is an integration test. Your best option would be create an ordered test to ensure that not exists concurrence when you execute your tests. – HuorSwords May 20 '14 at 07:07
  • One of the way is to create always a new dbname from "DB"+Guid.New().ToString().Replace("-","") – potehin143 May 20 '14 at 07:10
  • @potehin143 can you write code how to do it? – ilay zeidman May 20 '14 at 07:11
  • @HuorSwords I tried but I have exception that tells that I can't drop the database sometimes as I wrote in the question maybe I am doing something wrong... – ilay zeidman May 20 '14 at 07:13

2 Answers2

1

Please try this

[TestClass]
public class UnitTestClass
{
    private static string testConnString;


    [TestInitialize]
    public void Initialize()
    {
        testConnString = GetTestConnString();
        using (MyContext db = new MyContext(testConnString, new DropCreateDatabaseAlways<MyContext>()))
        {
            db.UnderlyingContext.Connection.Open();
        }

    }


    [TestMethod]
    public void TestMethod1()
    {
    }

    [TestCleanup]
    public void CleanUp()
    {

        using (MyContext db = new MyContext(testConnString))
        {
            db.Database.Delete();
        }
    }

    private static string GetTestConnString()
    {
        SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
        csb.DataSource = @"MYPC\SQLEXPRESS"; // Replace it with your SQL-server name
        csb.InitialCatalog = "DB"+Guid.NewGuid().ToString().Replace("-","");
        csb.IntegratedSecurity = true;
       return csb.ToString();
    }
}


public class MyContext : DbContext
{

    private static IDatabaseInitializer<MyContext> _Initializer;
    public MyContext(string connString, IDatabaseInitializer<MyContext> initializer = null)
        : base(connString)
    {
        _Initializer = initializer;
    }

    public ObjectContext UnderlyingContext
    {
        get { return (this as IObjectContextAdapter).ObjectContext; }
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        if (_Initializer != null)
        {
            Database.SetInitializer(_Initializer);
        }
        base.OnModelCreating(modelBuilder);
    }

}
potehin143
  • 541
  • 4
  • 9
1

What does "real" mean? Have you considered using an in-memory DB in your unit tests? With an in-memory DB like SQLite you can set up a fresh DB for each test with relatively little overhead. You can also prepare a test database once, store it to a file, and load it for each test. Check out this answer. And this link. You'll find more if you Google a little.

I would try to avoid relying on an ordering. The tests should be independent from each other to allow for clear traceability from failed tests to issues in the code. A test should not fail only because another test manipulated shared data.

Another concern for me would be performance. With a mandatory ordering parallel test execution is out of the question. And if you use a production-like DB setting for all your tests, they will definitely be much slower than with an in-memory replacement.

Community
  • 1
  • 1
EagleBeak
  • 6,939
  • 8
  • 31
  • 47