【发布时间】:2020-07-06 10:26:51
【问题描述】:
我有一个单元测试,它基本上是在测试 EF Core 的行为。我要测试的类如下所示:
namespace MusicPortal.Repository.Repository
{
public class ArtistRepository : IArtistRepository
{
private readonly MusicPortalDbContext _context;
public ArtistRepository(MusicPortalDbContext context)
{
_context = context;
}
public async Task<MusicPortalDatabaseResponse<bool>> AddNewArtist(Artist artist)
{
try
{
await _context.Artists.AddAsync(new Artist
{
ArtistType = ArtistTypes.Band,
City = artist.City,
Country = artist.Country,
Genre = artist.Genre,
Name = artist.Name,
ProfileImageUrl = artist.ProfileImageUrl
});
_context.SaveChanges();
return new MusicPortalDatabaseResponse<bool>
{
HasError = false,
Exception = null,
Response = true
};
}
catch (Exception e)
{
return new MusicPortalDatabaseResponse<bool>
{
HasError = true,
Exception = e,
Response = false
};
}
}
}
}
我使用 Moq 对其进行了以下单元测试
namespace MusicPortal.Tests.Repository.ArtistRepository.AddNewArtist
{
[TestFixture]
public class GivenAddingANewArtistToADatabaseFails
{
private Mock<DbSet<Artist>> _mockArtistDbSet;
private Mock<MusicPortalDbContext> _mockContext;
private IArtistRepository _artistRepository;
private MusicPortalDatabaseResponse<bool> _addArtistToDbResponse;
[OneTimeSetUp]
public async Task Setup()
{
_mockArtistDbSet = new Mock<DbSet<Artist>>();
_mockContext = new Mock<MusicPortalDbContext>();
_mockArtistDbSet
.Setup(x => x.AddAsync(It.IsAny<Artist>(), It.IsAny<CancellationToken>()))
.Callback((Artist artist, CancellationToken token) => { })
.ReturnsAsync(It.IsAny<EntityEntry<Artist>>());
_mockContext
.Setup(x => x.SaveChanges())
.Throws(new Exception("Cannot save new Artist to Database"));
_artistRepository = new MusicPortal.Repository.Repository.ArtistRepository(_mockContext.Object);
_addArtistToDbResponse = await _artistRepository.AddNewArtist(It.IsAny<Artist>());
}
[Test]
public void ThenANegativeResultIsReturned() // pass
{
Assert.IsFalse(_addArtistToDbResponse.Response);
Assert.IsTrue(_addArtistToDbResponse.HasError);
Assert.IsInstanceOf<Exception>(_addArtistToDbResponse.Exception);
}
[Test]
public void ThenTheArtistContextAddMethodIsCalledOnce() //fail
{
_mockArtistDbSet.Verify(x => x.AddAsync(It.IsAny<Artist>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public void ThenTheArtistsContextSaveMethodIsNeverCalled() //pass
{
_mockContext.Verify(x => x.SaveChanges(), Times.Never);
}
}
}
第一个和最后一个断言通过但ThenTheArtistContextAddMethodIsCalledOnce() 失败,因为以下错误:
MusicPortal.Tests.Repository.ArtistRepository.AddNewArtist.GivenAddingANewArtistToADatabaseFails.TheArtistContextAddMethodIsCalledOnce
Moq.MockException : 预期在模拟上调用一次,但为 0 次:x => x.AddAsync(It.IsAny(), It.IsAny())
执行的调用:
模拟:1> (x): 未执行任何调用。
在 Moq.Mock.Verify(Mock mock, LambdaExpression 表达式, Times times, String failMessage) 在 Moq.Mock
1.Verify[TResult](Expression1 表达式,Func`1 次) 在 MusicPortal.Tests\Repository\ArtistRepository\AddNewArtist\GivenAddingANewArtistToADatabaseFails.cs:line 53 中的 MusicPortal.Tests.Repository.ArtistRepository.AddNewArtist.GivenAddingANewArtistToADatabaseFails.ThenTheArtistContextAddMethodIsCalledOnce()
我了解到问题代码是c#
_mockArtistDbSet
.Setup(x => x.AddAsync(It.IsAny<Artist>(), It.IsAny<CancellationToken>()))
.Callback((Artist artist, CancellationToken token) => { })
.ReturnsAsync(It.IsAny<EntityEntry<Artist>>());
而且我知道问题很可能是由于异步问题造成的,但我不知道为什么,或者实际问题是什么。有什么建议和解决方案吗?
【问题讨论】:
-
你最后一次测试不应该也失败吗?看起来应该调用 SaveChanges。
-
该设置代码不应该真的存在,但不存在,Save 方法永远不会被调用,因为添加到 dbSet 的代码会引发错误并会延迟到 catch 块。
标签: c# unit-testing moq ef-core-2.2