我正在编写单元测试以测试更新EF核心实体的控制器操作。在单元测试中使用EF Core SqlLite时防止跟踪问题
我正在使用SQLLite,而不是嘲笑。
设置我的数据库是这样的:
internal static ApplicationDbContext GetInMemoryApplicationIdentityContext()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.Options;
var context = new ApplicationDbContext(options);
context.Database.EnsureCreated();
return context;
然后实体添加到数据库这样的:
private DiaryEntriesController _controller;
private ApplicationDbContext _context;
[SetUp]
public void SetUp()
{
_context = TestHelperMethods.GetInMemoryApplicationIdentityContext();
_controller = new DiaryEntriesController(_context);
}
[Test]
[Ignore("http://stackoverflow.com/questions/42138960/preventing-tracking-issues-when-using-ef-core-sqllite-in-unit-tests")]
public async Task EditPost_WhenValid_EditsDiaryEntry()
{
// Arrange
var diaryEntry = new DiaryEntry
{
ID = 1,
Project = new Project { ID = 1, Name = "Name", Description = "Description", Customer = "Customer", Slug = "slug" },
Category = new Category { ID = 1, Name = "Category" },
StartDateTime = DateTime.Now,
EndDateTime = DateTime.Now,
SessionObjective = "objective",
Title = "Title"
};
_context.DiaryEntries.Add(diaryEntry);
await _context.SaveChangesAsync();
var model = AddEditDiaryEntryViewModel.FromDiaryEntryDataEntity(diaryEntry);
model.Actions = "actions";
// Act
var result = await _controller.Edit(diaryEntry.Project.Slug, diaryEntry.ID, AddEditDiaryEntryViewModel.FromDiaryEntryDataEntity(diaryEntry)) as RedirectToActionResult;
// Assert
var retreivedDiaryEntry = _context.DiaryEntries.First();
Assert.AreEqual(model.Actions, retreivedDiaryEntry.Actions);
}
我控制器的方法是这样的:
[HttpPost]
[ValidateAntiForgeryToken]
[Route("/projects/{slug}/DiaryEntries/{id}/edit", Name = "EditDiaryEntry")]
public async Task<IActionResult> Edit(string slug, int id, [Bind("ID,CategoryID,EndDate,EndTime,SessionObjective,StartDate,StartTime,Title,ProjectID,Actions,WhatWeDid")] AddEditDiaryEntryViewModel model)
{
if (id != model.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
var diaryEntryDb = model.ToDiaryEntryDataEntity();
_context.Update(diaryEntryDb);
await _context.SaveChangesAsync();
return RedirectToAction("Details", new { slug = slug, id = id });
}
ViewData["CategoryID"] = new SelectList(_context.Categories, "ID", "Name", model.CategoryID);
ViewData["ProjectID"] = new SelectList(_context.Projects, "ID", "Customer", model.ProjectID);
return View(model);
}
我的问题是,当测试运行时,当我尝试更新实体时发生错误。我收到消息:
实体类型'DiaryEntry'的实例无法跟踪,因为具有相同键的此类型的另一个实例已被跟踪。
该代码在现实生活中运行良好。我坚持如何在测试中插入后停止跟踪,以便生产代码中的数据库上下文不仍然跟踪插入的实体。
我明白嘲笑接口到回购模式的好处,但我真的很想获得这种测试方法 - 我们将数据插入到内存数据库中,然后测试它已更新D b。
任何帮助将不胜感激。
感谢
编辑: 我说我的测试的完整的代码表明,我使用的是相同的上下文来创建数据库,并插入我与实例化控制器的日记。
谢谢@trevorc。我编辑问题以显示我实际上在控制器中使用了与测试的排列部分相同的数据库上下文。对不起,最初没有更清楚。 –
如果我不得不猜测罪魁祸首似乎是'model.ToDiaryEntryDataEntity()'。在您的控制器操作中,您没有从数据库中获取实体。您正在传递该实体的所有值,但您的扩展方法正在创建一个同样实体的新实例,这就是令人困惑的EF。您的控制器操作“有效”仅仅是因为您新创建的DiaryEntry不在DbContext中。在你的测试中,它是。 – trevorc
只是另一个建议。您的控制器编辑操作应首先获取实体,然后使用扩展方法将任何更改映射到属性,然后更新当前上下文中的实体。 – trevorc