Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Code First: How to seed a database for unit testing

My question and code is based on the Code First Entity Framework Unit Test Examples blog post. I am using SQL Compact 4.0 and as such my unit tests are running against the actual database using real data similar to what is being described in the blog post.

I want to seed my production database with default values in some of the tables but when running my unit tests I want to add additional data and update some of the default values.

I have created a custom Initializer class that seeds the database with the default values. For my unit tests I have created another custom Initializer that inherits from the first one that does the test specific seeding and/or modifications:

public class NerdDinnersInitializer : DropCreateDatabaseIfModelChanges<NerdDinners>
{
    protected override void Seed(NerdDinners context)
    {
        var dinners = new List<Dinner>
                          {
                              new Dinner()
                                  {
                                      Title = "Dinner with the Queen",
                                      Address = "Buckingham Palace",
                                      EventDate = DateTime.Now,
                                      HostedBy = "Liz and Phil",
                                      Country = "England"
                                  }
                          };

        dinners.ForEach(d => context.Dinners.Add(d));

        context.SaveChanges();
    }
}

public class NerdDinnersInitializerForTesting : NerdDinnersInitializer
{
    protected override void Seed(NerdDinners context)
    {
        base.Seed(context);

        var dinner = context.Dinners.Where(d => d.Country == "England").Single();
        dinner.Country = "Ireland";

        context.SaveChanges();
    }
}

I also use a base class for my unit tests that initializes the test database like so:

[TestClass]
public abstract class TestBase
{
    protected const string DbFile = "test.sdf";
    protected const string Password = "1234567890";
    protected NerdDinners DataContext;

    [TestInitialize]
    public void InitTest()
    {
        Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "",
                string.Format("Data Source=\"{0}\";Password={1}", DbFile, Password));
        Database.SetInitializer(new NerdDinnersInitializerForTesting());

        DataContext = new NerdDinners();
        DataContext.Database.Initialize(true);
    }

    [TestCleanup]
    public void CleanupTest()
    {
        DataContext.Dispose();

        if (File.Exists(DbFile))
        {
            File.Delete(DbFile);
        }
    }
}

The actual unit test looks like this:

[TestClass]
public class UnitTest1 : TestBase
{
    [TestMethod]
    public void TestMethod1()
    {
        var dinner = new Dinner()
                          {
                              Title = "Dinner with Sam",
                              Address = "Home",
                              EventDate = DateTime.Now,
                              HostedBy = "The wife",
                              Country = "Italy"
                          };

        DataContext.Dinners.Add(dinner);
        DataContext.SaveChanges();

        var savedDinner = (from d in DataContext.Dinners
                           where d.DinnerId == dinner.DinnerId
                           select d).Single();

        Assert.AreEqual(dinner.Address, savedDinner.Address);
    }
}

When I run the test the Linq query that fetches the savedDinner fails with the "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." exception. I cannot work out why.

Is what I am doing here an acceptable pattern and can anyone shed some light as to why this is not working?

Thanks.

like image 901
Gerhard Wessels Avatar asked Oct 24 '22 04:10

Gerhard Wessels


1 Answers

I ran into a similar issue this morning. The problem is caused by the where clause in the seed method. A workaround for this (for now) is rewriting this:

var dinner = context.Dinners.ToList().Where(d => d.Country == "England").Single(); 

Although not efficient (all objects are retrieved from the database and filtering will be done in memory), it did solve the ObjectDisposedException in my unit tests. In my case I only have a few objects, so I can live with it for now.

like image 164
Jos van Velzen Avatar answered Oct 27 '22 09:10

Jos van Velzen