Microsoft InMemoryDatabase 在测试期间未连接实体框架中的关系

问题描述 投票:0回答:1

测试结果应该是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); 
}

我不知道该怎么办。

.net entity-framework testing mocking
1个回答
0
投票

这是一个已知问题/限制,已引入 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 操作。

© www.soinside.com 2019 - 2024. All rights reserved.