测试结果应该是3,但结果是零。这是因为在 Get 方法中,
.Include()
线未将 Task
连接到 Entries
。但在汽车的调试模式下,它们似乎已连接。这适用于实时代码,所以我认为这是内存数据库配置的问题。
获取方法:
public IEnumerable<Entry> Get(string userId, DateTime from, DateTime to)
{
List<Entry> result;
try
{
result = _db.Entries
.Include(n => n.Task.ApplicationUser) //that's where InMemoryDb doesn't connect Task to Entries
.Where(n => n.Task.ApplicationUserId == userId).Select(n => n)
.Where(n => (n.Created.Date >= from.Date) && (n.Created.Date <= to.Date))
.ToList();
}
catch (Exception e)
{
_logger.LogError($"Exception: {e.Message} StackTrace: {e.StackTrace}");
return new List<Entry>();
}
return result;
};
内存数据库配置:
private async Task<TasksContext> GetDbContext()
{
var options = new DbContextOptionsBuilder<TasksContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
// creating DBcontext from mock
var dbContext = new TasksContext(options);
dbContext.Database.EnsureDeleted();
dbContext.Entries.AsNoTracking();
dbContext.Tasks.Add(new Task { Id = 1, Name = "RunningId1", GroupId = 4, Created = _createdFirstEntry, ApplicationUserId = _userId });
dbContext.Tasks.Add(new Task { Id = 2, Name = "CookingId2", GroupId = 4, Created = _createdSecondEntry, ApplicationUserId = _userId });
dbContext.Tasks.Add(new Task { Id = 3, Name = "ReadingId3", GroupId = 4, Created = _createdThirdEntry, ApplicationUserId = _userId });
dbContext.Entries.Add(new Entry { Id = _entryId_1, TaskId = _taskId_1, Duration = _duration, Created = _createdFirstEntry, LastUpdated = null, Deleted = null });
dbContext.Entries.Add(new Entry { Id = _entryId_2, TaskId = _taskId_2, Duration = _duration, Created = _createdSecondEntry, LastUpdated = null, Deleted = null });
dbContext.Entries.Add(new Entry { Id = _entryId_3, TaskId = _taskId_3, Duration = _duration, Created = _createdThirdEntry, LastUpdated = null, Deleted = null });
await dbContext.SaveChangesAsync();
return dbContext;
}
测试方法:
public async void Get_TakesValidUserIdAndDateRange_ReturnsListWithThreeElements()
{
// Arrange
var dbContext = await GetDbContext();
var entryRepository = new EntryRepository(dbContext, _logger, _mapper);
// Act
var result = entryRepository.Get(_userId,_createdFirstEntry, _createdThirdEntry);
// Assert
result.Should().BeOfType(typeof(List<Entry>));
result.Count().Should().Be(3);
}
我不知道该怎么办。
这是一个已知问题/限制,已引入 InMemory 提供程序,并且似乎与使用具有
[Required]
属性的 Nullable 导航属性有关。
有关更改的一些讨论: https://github.com/dotnet/efcore/issues/9470 https://github.com/dotnet/EntityFramework.Docs/pull/3512
所以看来:
[Required]
public Task? Task { get; set; }
...行不通,但是:
public Task Task { get; set; }
应该有可能。尽管您会收到 CS8618 编译器警告。对于实体,我只是使用受保护的默认构造函数和警告忽略来缓解此问题:
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
/// <summary>
/// Constructor used by EF.
/// </summary>
[ExcludeFromCodeCoverage]
protected Entry() {}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
InMemory 数据库提供程序不再受到积极支持,并且除了简单的测试场景外不推荐使用。对于单元测试,您应该考虑一种可模拟的模式,其中集成测试将针对数据库副本运行。
在支持单元测试时,我使用公开的存储库模式
IQueryable<TEntity>
。假设您的“Get”方法还不是存储库(在这种情况下,这将是模拟的边界),从那里可以模拟存储库,您可以使用 MockQueryable.Core 之类的东西(https://www.nuget)。 org/packages/MockQueryable.Core)来包装测试数据的示例集合,以使用同步和异步 Linq 操作。