使用实体框架数据库上下文进行单元测试:不重置ID计数器

时间:2019-03-15 13:46:10

标签: c# unit-testing asp.net-core entity-framework-core

我正在尝试编写一个基于ASP.Net核心的相当简单的Web服务的单元测试。为了持久化,我使用实体框架核心来访问SQL数据库。

在单元测试中,我显然不希望数据库可以从一个测试渗入另一个测试。经过一番搜索,我发现了这篇文章https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory。我的测试代码现在看起来像这样:

[Fact]
public void Test_method()
{    
    var options = new DbContextOptionsBuilder<ServiceContext>()
        .UseInMemoryDatabase(databaseName: "Test_method") // Unique name for each test
        .Options;

    using (var context = new ServiceContext(options))
    {
        // Add test data
        context.Dataset.Add(new ...);
        context.SaveChanges();
    }

    using (var context = new ServiceContext(options))
    {
        // Perform tests
        var controller = new Controller(new Service(context));
        ...
    }
}

这几乎可行,每个测试用例都以一个空的DB开始。但是testdata元素分配的ID不会重置。因此,如果我有一个将一个元素添加到数据库中的测试,而另一个有两个元素添加了测试,则第一个测试中的测试元素可能会获得ID 1或3,具体取决于这两个测试的执行顺序。

有没有一种方法可以确保ID在单个测试方法中始终以1开头?当ID取决于其他测试是否在当前测试之前进行时,执行基于ID的查找的测试代码确实非常丑陋。

2 个答案:

答案 0 :(得分:1)

Entity Framework inmemory database不会重置其自动增量计数器。

以下是Github问题的声明:

The InMemory provider doesn't the Identity pattern you would get in a relational database. When a column is configured as ValueGeneratedOnAdd it uses values from a single generator for all tables. This would be similar to having all tables setup to use a single sequence in a relational database. This raises one of the important design principles of our InMemory provider, which is that we are not trying to emulate a relational database. This becomes more important as we start to light up non-relational providers.

If you want to have a database that acts like a relational database, but doesn't have the overhead of I/O, then we'd recommend using an In-Memory SQLite database - http://www.sqlite.org/inmemorydb.html.

We're going to update our testing docs to make the SQLite option more prominent.

来源:https://github.com/aspnet/EntityFrameworkCore/issues/6872

因此,您可以考虑在每次运行测试时手动重置计数器(类似于ALTER TABLE mytable AUTO_INCREMENT = 1;),或使用帖子中提到的其他sql提供程序。

答案 1 :(得分:0)

只需使用诸如Guid.NewGuid().ToString()之类的随机名称作为数据库名称,每次都会重置所有内容,因为它将成为内存中一个全新的“数据库”。

var options = new DbContextOptionsBuilder<ServiceContext>()
    .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) // Unique name for each test
    .Options;