简体   繁体   中英

How to properly mock extension methods with generics in xUnit?

So I'm trying to mock a service of mine, here's the real code:

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);
        }
}

It uses an Extension Method, this is here:

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;
        }
}

Here's my xUnit test:

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);
        }
}

The error I'm getting is in the Setup of the _repository:

repository.Setup(x => RepositoryExtensions.GetActiveAsync<Phase>(_repository, It.IsAny<Guid>()))
                    .Returns(GetSamplePhase(newGuid));  

It says it cannot convert the Mocked repository to a real one, but shouldn't I use the mocked repository here?

What I'm trying to achieve is testing my REAL service and mocking the repository, right? Am I doing it correctly here?

Assuming you are using MOQ, do not try to mock the extension method.

Since you control the code of the extension method then mock a safe path through the extension method.

The extension uses GetAsync in this case and that is what needs to be mocked assuming that is not an extension as well.

//...

 _repository
    .Setup(x => x.GetAsync(It.IsAny<Guid>()))
    .ReturnsAsync(GetSamplePhase(newGuid));

//...

It will allow the test when exercised to go through GetActiveAsync code and if it fails, also throw the Exceptions etc as described in the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM