实体框架迁移 - 如何创建单元测试以确保迁移模型是最新的?

Nig*_*888 8 continuous-integration unit-testing entity-framework database-migration ef-migrations

我正在使用与TeamCity,NUnit和Git的持续集成.我最近从FluentMigrator迁移(赦免双关语)到实体框架迁移.我主要是为了利用其中的脚手架功能.

但是,有可能检查源控件的某些更改,而无需先将更改支持迁移(假设在提交和推送提交之前未运行应用程序的方案).我正在使用预先测试的提交工作流程,所以我想在预测试中检测到这个问题,而不是等到调用migrate.exe(这在我的工作流程中为时已晚并打破"绿色存储库").

我的问题是,如何创建单元/集成测试来检测迁移模型何时与上下文模型不匹配,以便在尝试迁移之前我可以使构建失败?请注意,我希望它不匹配数据库,并且不希望从测试中访问数据库.

我试过这种方法:

    [Test]
    public void CheckWhetherEntityFrameworkMigrationsContextIsUpToDate()
    {
        Assert.DoesNotThrow(() =>
        {
            CallDatabase();
        });

    }

    private void CallDatabase()
    {
        using (var ctx = new MyContext("SERVER=(local);DATABASE=MyDatabase;Integrated Security=True;"))
        {
            var tenant = (from t in ctx.Tenant
                          select t).FirstOrDefault();
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是,当存在挂起的迁移时,这总是会失败(而不仅仅是在迁移模型与上下文模型不同步的情况下,这就是我所追求的).

更新

我已在EntityFramework项目中为此问题添加了一个工作项.希望他们会考虑添加一种方法来实现这一目标.

Pab*_*meo 11

如果有人发现这个有用,我会使用以下代码通过针对测试数据库运行所有迁移来测试迁移:

[TestClass]
public class MigrationsTests
{
    [TestMethod]
    public void RunAll()
    {
        var configuration = new Configuration();
        var migrator = new DbMigrator(configuration);
        // back to 0
        migrator.Update("0");
        // up to current
        migrator.Update();
        // back to 0
        migrator.Update("0");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将测试所有UpDown迁移,以及在关闭自动迁移时检测挂起的更改.

注意:确保对测试数据库运行此操作.在我的例子中,测试项目app.config具有到测试数据库的connectionString(只是一个本地SQLExpress实例).

  • 我不会说它不是一个好的测试.它只是上下完整的链条,但是,如果你愿意,你可以做个人上下.我没有遇到你提到TBH的情况. (2认同)

bri*_*lam 5

这是一些代码,将检查所有模型更改是否都已被移植到迁移中。

bool HasPendingModelChanges()
{
    // NOTE: Using MigratorScriptingDecorator so changes won't be made to the database
    var migrationsConfiguration = new Migrations.Configuration();
    var migrator = new DbMigrator(migrationsConfiguration);
    var scriptingMigrator = new MigratorScriptingDecorator(migrator);

    try
    {
        // NOTE: Using InitialDatabase so history won't be read from the database
        scriptingMigrator.ScriptUpdate(DbMigrator.InitialDatabase, null);
    }
    catch (AutomaticMigrationsDisabledException)
    {
        return true;
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)


bbo*_*ler 5

到目前为止,我想要这里给出的所有答案中最好的,所以这是我想出的:

[TestClass()]
public class MigrationsTests
{
    [TestMethod()]
    public void MigrationsUpDownTest()
    {
        // Unit tests don't have a DataDirectory by default to store DB in
        AppDomain.CurrentDomain.SetData("DataDirectory", System.IO.Directory.GetCurrentDirectory());

        // Drop and recreate database
        BoxContext db = new BoxContext();
        db.Database.Delete();

        var configuration = new Migrations.Configuration();
        var migrator = new DbMigrator(configuration);

        // Retrieve migrations
        List<string> migrations = new List<string>;
        migrations.AddRange(migrator.GetLocalMigrations());

        try
        {
            for (int index = 0; index < migrations.Count; index++)
            {
                migrator.Update(migrations[index]);
                if (index > 0) {
                    migrator.Update(migrations[index - 1]);
                } else {
                    migrator.Update("0"); //special case to revert initial migration
                }
            }

            migrator.Update(migrations.Last());
        }
        catch (SqlException ex)
        {
            Assert.Fail("Should not have any errors when running migrations up and down: " + ex.Errors[0].Message.ToString());
        }

        // Optional: delete database
        db.Database.Delete();
    }

    [TestMethod()]
    public void PendingModelChangesTest()
    {
        // NOTE: Using MigratorScriptingDecorator so changes won't be made to the database
        var migrationsConfiguration = new Migrations.Configuration();
        var migrator = new DbMigrator(migrationsConfiguration);
        var scriptingMigrator = new MigratorScriptingDecorator(migrator);

        try
        {
            // NOTE: Using InitialDatabase so history won't be read from the database
            scriptingMigrator.ScriptUpdate(DbMigrator.InitialDatabase, null);
        }
        catch (AutomaticMigrationsDisabledException)
        {
            Assert.Fail("Should be no pending model changes/migrations should cover all model changes.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

值得注意的几件事:

  • 您需要在测试项目中安装实体框架
  • 您需要添加[assembly: InternalsVisibleTo("MyTestsProject")]Configuration班级的顶部
  • 您需要指定一个App.config仅用于测试项目的实体框架连接字符串,因为数据库将被频繁删除和重新创建 - 如果在构建机器上运行请记住并行运行可能会导致冲突,因此您可能需要更改每个构建的字符串