我正在使用EntityFrameworkCore 1.0并试图设置一些简单的单元测试,但是不断收到错误,结果是“已经添加了一个具有相同键的项目。键:-10。我不明白如何防止这种情况发生。我明白问题是什么,但不知道为什么以及如何解决。
首先,我的测试看起来像是:
public void CanLoadAllAnnouncements()
{
var service = new AnnouncementControllerService(GenerateTestData.Generate());
var results = service.Get();
Assert.Equal(3, results.Count);
Assert.Equal("Test Announcement 1", results[0].Message);
Assert.Equal("Test Announcement 2", results[1].Message);
Assert.Equal("Test Announcement 3", results[2].Message);
}
您可以看到这是在调用正在生成的内存数据库中的控制器服务类。这个类看起来像这样的代码。
public const string CurrentUserName = "testUserName";
public const string AlternateUserName = "anotherUserName";
public static DataContext Generate()
{
var context = new DataContext();
CreateRecentActivity(context);
CreateAnnouncement(context);
context.SaveChanges();
return context;
}
private static void CreateAnnouncement(DataContext context)
{
AddAnnouncement(context, -20, "Test Announcement 1", 1);
AddAnnouncement(context, -21, "Test Announcement 2", 2);
AddAnnouncement(context, -22, "Test Announcement 3", 3);
}
private static void CreateRecentActivity(DataContext context)
{
AddRecentActivity(context, -10, "Test Result 1", "#/TestResult1", CurrentUserName);
AddRecentActivity(context, -11, "Test Result 2", "#/TestResult2", CurrentUserName);
AddRecentActivity(context, -12, "Test Result 3", "#/TestResult3", CurrentUserName);
AddRecentActivity(context, -13, "Another Test Result 1", "#/AnotherTestResult1", AlternateUserName);
AddRecentActivity(context, -14, "Another Test Result 2", "#/AnotherTestResult2", AlternateUserName);
AddRecentActivity(context, -15, "Another Test Result 3", "#/AnotherTestResult3", AlternateUserName);
}
private static void AddAnnouncement(DataContext context, int id, string message, int ordering)
{
if (context.Announcements.All(ra => ra.Id != id))
{
context.Announcements.Add(new Announcement
{
Id = id,
Message = message,
Ordering = ordering
});
}
}
private static void AddRecentActivity(DataContext context, int id, string name, string url, string userName)
{
if (context.RecentActivities.All(ra => ra.Id != id))
{
context.RecentActivities.Add(new RecentActivity
{
Id = id,
Name = name,
Url = url,
UserName = userName
});
}
}
所以你可以看到它只是在检查它还没有被添加之后向每个DbSet添加几个项目。现在,如果我运行此测试,它将全天工作,此时没有任何问题。
当我添加第二个测试时,问题就出现了,就像这样
public void CanLoadAllRecentItemsForCurrentUser()
{
var service = new RecentActivityControllerService(GenerateTestData.Generate());
var results = service.Get(Testing.GenerateTestData.CurrentUserName);
Assert.Equal(3, results.Count);
Assert.Equal("Test Result 1", results[0].Name);
Assert.Equal("Test Result 2", results[1].Name);
Assert.Equal("Test Result 3", results[2].Name);
}
您可以看到此测试与前一测试非常相似。执行完全相同的操作,创建控制器服务,传入在generate方法中构建的db上下文的新实例。
这是出现错误的地方。我假设问题是以某种方式将上下文生成为静态(即使我每次都生成一个单独的实例)。由于测试是并行运行的,因此使用相同的密钥添加相同的项目会导致错误。
我已经尝试删除GenerateTestData类中的所有静态内容(类,方法等)并使用实例变量,但这没有任何区别。
我在这里缺少什么。我想为每个测试生成一个单独的内存数据库,以便它们之间没有依赖关系。
答案 0 :(得分:4)
我在链接https://docs.efproject.net/en/latest/miscellaneous/testing.html
中找到了答案基本思想是你需要在数据上下文的构造函数中指定DbContextOptions,以确保它为每个测试创建一个单独的干净上下文。
public static DataContext Generate()
{
var options = CreateNewContextOptions();
var context = new DataContext(options);
CreateRecentActivity(context);
CreateAnnouncement(context);
context.SaveChanges();
return context;
}
private static DbContextOptions<DataContext> CreateNewContextOptions()
{
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<DataContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}