数据库初始化运行太晚了

时间:2014-09-19 14:11:15

标签: c# linq entity-framework unit-testing ef-code-first

EDITED

我正在运行单元测试(使用nunit)进行数据库初始化方法。我设置了测试并使用以下测试示例为数据库播种:

    [TestFixtureSetUp]
    public void SetUp()
    {
        //select database
        Database.DefaultConnectionFactory = new SqlConnectionFactory(connectionString);
        database = new POSDatabaseContext(connectionString);

        //drop database and recreate
        string query = "ALTER DATABASE [POS] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;";
        database.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, query);
        database.Database.Delete();
        database.Database.Create();

        //add seed value to test against
        database.Metadata.Add(new Metadata {
            ID = "META",
            IsInitialized = false,
            testBool = true,
            TimeCreated = DateTime.Now,
            TimeEdited = DateTime.Now
        });
        database.SaveChanges();
    }
然后我运行测试方法:

    [Test]
    public void InitializeDatabaseTestWithMatchingModel()
    {
        //set initializer for data context to test it, and run it
        Database.SetInitializer<POSDatabaseContext>(new
            CustomDropCreateDatabaseWithMatchingModelTest());
        database.Database.Initialize(true);

        //use the metadata table to check if it was run correctly
        //if metadata exist, which it should
        if(database.Metadata.Any(s => s.ID == "META"))
        {
            Metadata actual = database.Metadata.Single(s => s.ID == "META");
            Assert.IsTrue(actual.IsInitialized);
        }
        else
            throw new Exception("The Database was not seeded correctly for the test");
    }

为了测试我的自定义初始化程序,我创建了一个继承自它的类,我可以使用Seed()方法生成一些我可以测试的数据。这是班级:

    //class is derived from class that needs to be tested so can still be used for testing
    //this class adds seed data to check the database with
    public class CustomDropCreateDatabaseWithMatchingModelTest 
        : CustomDropCreateDatabaseIfModelChanges<POSDatabaseContext>
    {
        protected override void Seed(POSDatabaseContext context)
        {
            //if metadata exist (Which it should since the database
            //shouldn't be dropped because the model is the same)
            if(context.Metadata.Any(s => s.ID == "META"))
            {
                Metadata meta = context.Metadata.Single(s => s.ID == "META");
                if(meta.IsInitialized == true)
                    throw new Exception("The database has not been dropped"+
                        " and recreated correctly during the unit test setup."
                    );
                else
                {
                    meta.IsInitialized = true;
                    meta.TimeEdited = DateTime.Now;
                    context.SaveChanges();
                }
            }
            else
                throw new Exception("Metadata not found. The database was"+
                    " either dropped because it was falsely accused of not"+
                    " having a matching model, OR the database was not seeded"+
                    " properly during unit test setup."
                    );
        }
    }

它来自的课程:

//This file contains custom versions of the built in
//database intializer classes in which an SQL Statement
//is run to stop all connections to the database so that
//when the database is dropped, a database in use
//exception is not thrown.
public class CustomDropCreateDatabaseIfModelChanges<Context> 
    : IDatabaseInitializer<Context> where Context : DbContext
{
    public void InitializeDatabase(Context context)
    {
        if (context.Database.Exists())
        {
            if (!context.Database.CompatibleWithModel(true))
            {
                string query = "ALTER DATABASE [POS] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;";
                context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, query);
                context.Database.Delete();
                context.Database.Create();
            }
            //else do nothing and continue
        }
        else context.Database.Create();

        //run seed method
        Seed(context);
    }

    //OVERIDE ME
    //this method seeds the database with data
    protected virtual void Seed(Context context) { }
}

我似乎遇到的问题是,在断言之后,Initilization才会运行。我检查了元数据实际的值,值是SetUp()方法的值。我还检查了在我测试的方法中调用SaveChanges()之前对象被标记为已修改。但是,该方法肯定是在运行,因为数据库反映了方法的值。

我认为我遇到的原始问题是IsInitialized属性被返回为错误值,因为它在数据库中为1,但我在代码中得到了错误。

2 个答案:

答案 0 :(得分:1)

Assert.AreEqual(true,更改为Assert.IsTrue(,因为true类型之间可以有不同的格式。正如您所注意到的,它在数据库中编码为1,可能不完全匹配。

您可以检查SaveChanges()是否正确更新Assert之前的上下文:首先在TimeEdited上执行断言。 (将DateTime.Now替换为一些更容易比较的常量值。)

答案 1 :(得分:0)

在我的测试课程的顶部,我有:

[TestFixture]
public class CustomDropCreateDatabaseIfModelChangesTest
{
    protected const string connectionString = 
        "Data Source=SABERTOOTH\\POS;Initial Catalog=POS;Integrated Security=true;Pooling=false";
    protected POSDatabaseContext database;

....

}

我已经更改了测试以使用新的DbContext:

    [Test]
    public void InitializeDatabaseTestWithMatchingModel()
    {
        //set initializer for data context to test it, and run it
        Database.SetInitializer<POSDatabaseContext>(new CustomDropCreateDatabaseWithMatchingModelTest());
        database.Database.Initialize(true);

        POSDatabaseContext newContext = new POSDatabaseContext(connectionString);

        //use the metata table to check if it was run correctly
        //if metadata exist, which it should
        if(newContext.Metadata.Any(s => s.ID == "META"))
        {
            Metadata actual = newContext.Metadata.Single(s => s.ID == "META");

            Assert.IsTrue(actual.IsInitialized);
        }
        else
            throw new Exception("The Database was not seeded correctly");         
    }

事实证明,Pieter21是正确的,因为某些原因,上下文没有得到刷新,并且必须创建新的上下文。但我不确定为什么。