如何在 xUnit 中使用泛型正确模拟扩展方法?
How to properly mock extension methods with generics in xUnit?
所以我正在尝试模拟我的服务,这是真正的代码:
public class PhaseService : IPhaseService
{
private readonly IRepository<Phase> _phaseRepository;
private readonly IMapper _mapper;
private readonly HrbContext _context;
public PhaseService(IRepository<Phase> phaseRepository, IMapper mapper, HrbContext context)
{
_phaseRepository = phaseRepository;
_mapper = mapper;
_context = context;
}
public async Task<PhaseDto> GetAsync(Guid id)
{
var result = await _phaseRepository.GetActiveAsync(id);
return _mapper.Map<PhaseDto>(result);
}
}
它使用了扩展方法,在这里:
namespace HRB_Server.Application.Extensions
{
public static class RepositoryExtensions
{
/// <summary>
/// Returns the entity to which the given id is a match (no navigation properties loaded). Throws exceptions if the entity is not found or if is not active.
/// </summary>
public static async Task<T> GetActiveAsync<T>(this IRepository<T> repo, Guid id)
where T : BaseEntity
{
T entity = await repo.GetAsync(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(T), id);
}
if (!entity.IsActive)
{
throw new EntityNotActiveException(typeof(T), id);
}
return entity;
}
}
这是我的 xUnit 测试:
namespace HRB_Server.Tests.Services
{
public class PhaseServiceTest
{
private readonly Mock<IRepository<Phase>> _repository;
private readonly Mock<IMapper> _mapper;
private readonly Mock<HrbContext> _context;
public PhaseServiceTest()
{
_repository = new Mock<IRepository<Phase>>();
//_mapper = new Mock<IMapper>();
_mapper = null;
//_context = new Mock<HrbContext>(new DbContextOptions<HrbContext>(), new HttpContextAccessor());
_context = null;
}
[Fact]
public void GetPhase_ActivePhaseObject_PhaseShouldExist()
{
// Arrange
var newGuid = Guid.NewGuid();
var phase = GetSamplePhase(newGuid);
_repository.Setup(x => RepositoryExtensions.GetActiveAsync<Phase>(_repository, It.IsAny<Guid>()))
.Returns(GetSamplePhase(newGuid));
var phaseService = new PhaseService(_repository.Object, _mapper.Object, _context.Object);
// Act
var result = phaseService.GetAsync(newGuid);
// Assert (expected, actual)
Assert.Equal(phase.Result.Id, newGuid);
}
}
我遇到的错误是在 _repository 的设置中:
repository.Setup(x => RepositoryExtensions.GetActiveAsync<Phase>(_repository, It.IsAny<Guid>()))
.Returns(GetSamplePhase(newGuid));
它说它不能将模拟存储库转换为真实存储库,但我不应该在这里使用模拟存储库吗?
我想要实现的是测试我的真实服务并模拟存储库,对吗?我在这里做得对吗?
假设您使用的是最小起订量,请勿尝试模拟扩展方法。
既然你控制了扩展方法的代码,那么通过扩展方法模拟一个安全路径。
扩展在这种情况下使用 GetAsync
,假设它也不是扩展,这就是需要模拟的内容。
//...
_repository
.Setup(x => x.GetAsync(It.IsAny<Guid>()))
.ReturnsAsync(GetSamplePhase(newGuid));
//...
它将允许测试在执行时通过 GetActiveAsync
代码,如果失败,也会抛出代码中描述的异常等。
所以我正在尝试模拟我的服务,这是真正的代码:
public class PhaseService : IPhaseService
{
private readonly IRepository<Phase> _phaseRepository;
private readonly IMapper _mapper;
private readonly HrbContext _context;
public PhaseService(IRepository<Phase> phaseRepository, IMapper mapper, HrbContext context)
{
_phaseRepository = phaseRepository;
_mapper = mapper;
_context = context;
}
public async Task<PhaseDto> GetAsync(Guid id)
{
var result = await _phaseRepository.GetActiveAsync(id);
return _mapper.Map<PhaseDto>(result);
}
}
它使用了扩展方法,在这里:
namespace HRB_Server.Application.Extensions
{
public static class RepositoryExtensions
{
/// <summary>
/// Returns the entity to which the given id is a match (no navigation properties loaded). Throws exceptions if the entity is not found or if is not active.
/// </summary>
public static async Task<T> GetActiveAsync<T>(this IRepository<T> repo, Guid id)
where T : BaseEntity
{
T entity = await repo.GetAsync(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(T), id);
}
if (!entity.IsActive)
{
throw new EntityNotActiveException(typeof(T), id);
}
return entity;
}
}
这是我的 xUnit 测试:
namespace HRB_Server.Tests.Services
{
public class PhaseServiceTest
{
private readonly Mock<IRepository<Phase>> _repository;
private readonly Mock<IMapper> _mapper;
private readonly Mock<HrbContext> _context;
public PhaseServiceTest()
{
_repository = new Mock<IRepository<Phase>>();
//_mapper = new Mock<IMapper>();
_mapper = null;
//_context = new Mock<HrbContext>(new DbContextOptions<HrbContext>(), new HttpContextAccessor());
_context = null;
}
[Fact]
public void GetPhase_ActivePhaseObject_PhaseShouldExist()
{
// Arrange
var newGuid = Guid.NewGuid();
var phase = GetSamplePhase(newGuid);
_repository.Setup(x => RepositoryExtensions.GetActiveAsync<Phase>(_repository, It.IsAny<Guid>()))
.Returns(GetSamplePhase(newGuid));
var phaseService = new PhaseService(_repository.Object, _mapper.Object, _context.Object);
// Act
var result = phaseService.GetAsync(newGuid);
// Assert (expected, actual)
Assert.Equal(phase.Result.Id, newGuid);
}
}
我遇到的错误是在 _repository 的设置中:
repository.Setup(x => RepositoryExtensions.GetActiveAsync<Phase>(_repository, It.IsAny<Guid>()))
.Returns(GetSamplePhase(newGuid));
它说它不能将模拟存储库转换为真实存储库,但我不应该在这里使用模拟存储库吗?
我想要实现的是测试我的真实服务并模拟存储库,对吗?我在这里做得对吗?
假设您使用的是最小起订量,请勿尝试模拟扩展方法。
既然你控制了扩展方法的代码,那么通过扩展方法模拟一个安全路径。
扩展在这种情况下使用 GetAsync
,假设它也不是扩展,这就是需要模拟的内容。
//...
_repository
.Setup(x => x.GetAsync(It.IsAny<Guid>()))
.ReturnsAsync(GetSamplePhase(newGuid));
//...
它将允许测试在执行时通过 GetActiveAsync
代码,如果失败,也会抛出代码中描述的异常等。