【发布时间】:2018-12-04 00:20:45
【问题描述】:
我有一些如下代码,我想编写单元测试我的方法。但我被困在异步方法中。你能帮我吗 ?
public class Panel
{
public int Id { get; set; }
[Required] public double Latitude { get; set; }
public double Longitude { get; set; }
[Required] public string Serial { get; set; }
public string Brand { get; set; }
}
public class CrossSolarDbContext : DbContext
{
public CrossSolarDbContext()
{
}
public CrossSolarDbContext(DbContextOptions<CrossSolarDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
public interface IGenericRepository<T>
{
Task<T> GetAsync(string id);
IQueryable<T> Query();
Task InsertAsync(T entity);
Task UpdateAsync(T entity);
}
public abstract class GenericRepository<T> : IGenericRepository<T>
where T : class, new()
{
protected CrossSolarDbContext _dbContext { get; set; }
public async Task<T> GetAsync(string id)
{
return await _dbContext.FindAsync<T>(id);
}
public IQueryable<T> Query()
{
return _dbContext.Set<T>().AsQueryable();
}
public async Task InsertAsync(T entity)
{
_dbContext.Set<T>().Add(entity);
await _dbContext.SaveChangesAsync();
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
}
public interface IPanelRepository : IGenericRepository<Panel> { }
public class PanelRepository : GenericRepository<Panel>, IPanelRepository
{
public PanelRepository(CrossSolarDbContext dbContext)
{
_dbContext = dbContext;
}
}
[Route("[controller]")]
public class PanelController : Controller
{
private readonly IPanelRepository _panelRepository;
public PanelController(IPanelRepository panelRepository)
{
_panelRepository = panelRepository;
}
// GET panel/XXXX1111YYYY2222
[HttpGet("{panelId}")]
public async Task<IActionResult> Get([FromRoute] string panelId)
{
Panel panel = await _panelRepository.Query().FirstOrDefaultAsync(x => x.Serial.Equals(panelId, StringComparison.CurrentCultureIgnoreCase));
if (panel == null) return NotFound();
return Ok(panel);
}
}
public class PanelControllerTests
{
private readonly PanelController _panelController;
private static readonly Panel panel = new Panel { Id = 1, Brand = "Areva", Latitude = 12.345678, Longitude = 98.7655432, Serial = "AAAA1111BBBB2222" };
private readonly IQueryable<Panel> panels = new List<Panel>() { panel }.AsQueryable();
private readonly Mock<IPanelRepository> _panelRepositoryMock = new Mock<IPanelRepository>();
public PanelControllerTests()
{
_panelRepositoryMock.Setup(x => x.Query()).Returns(panels);
// I also tried this. I got another error 'Invalid setup on an extension method: x => x.FirstOrDefaultAsync<Panel>(It.IsAny<Expression<Func<Panel, Boolean>>>(), CancellationToken)'
// _panelRepositoryMock.As<IQueryable<Panel>>().Setup(x => x.FirstOrDefaultAsync(It.IsAny<Expression<Func<Panel, bool>>>(), default(CancellationToken))).ReturnsAsync(panel);
_panelController = new PanelController(_panelRepositoryMock.Object);
}
[Fact]
public async Task Register_ShouldInsertOneHourElectricity()
{
IActionResult result = await _panelController.Get("AAAA1111BBBB2222");
Assert.NotNull(result);
var createdResult = result as CreatedResult;
Assert.NotNull(createdResult);
Assert.Equal(201, createdResult.StatusCode);
}
}
我收到了这个错误
源 IQueryable 的提供程序未实现 IAsyncQueryProvider。只有实现 IEntityQueryProvider 的提供程序才能用于实体框架异步操作。
我认为我需要模拟“FirstOrDefaultAsync”,但我不确定,也不知道该怎么做。我尝试了一些东西,但它无法编译。
【问题讨论】:
-
注意确实是一个答案,但一个不相关的提示:如果方法中的 only 事物
async是最后一行的await,您几乎总是可以删除方法中的async修饰符和下游任务的return- 即return _dbContext.SaveChangesAsync();(不是await) - 这将删除Task间接级别并避免方法中的异步状态机。 -
@MarcGravell 非常感谢您的关注。其实
Get方法并没有这么短。它还包含其他操作。我这样做是为了更好地理解主要目的。很抱歉造成混乱。 -
您最好使用控制器操作进行集成测试。控制器需要大量设置和模拟才能通过单元测试正确运行,这几乎会使单元测试无效。它失败是因为它实际上失败了还是因为你没有正确地模拟某些事情?另一方面,它实际上可能会通过您的模拟,但在真正的请求管道中运行时会失败,因为同样,您可能没有正确模拟所有内容。而是使用测试主机:docs.microsoft.com/en-us/aspnet/core/test/…
-
@MarcGravell 我实际上避免省略异步,因为它会破坏异常处理。
标签: c# unit-testing entity-framework-6 asp.net-core-2.0 iqueryable