moq icon indicating copy to clipboard operation
moq copied to clipboard

C# Xunit & Moq mocking generic repository, unitOfWork repository always returns null

Open deva7mad opened this issue 3 years ago • 1 comments

The problem is when I try to mock any method of the repository, for example GetFirstOne, it always return null during debugging.

C# mocking generic repository, unitOfWork and applying filter expression return exception that because repository always returns null

Method in service class that I wrote test for it:

    public bool ResetPassword(ChangePasswordDto changePasswordDto)
    {
        var userQueryCriteria = UserOfMobileQueryCriteria(changePasswordDto.MobileNumber);
        
        var user = _unitOfWork.UserRepository.GetFirstOne(userQueryCriteria);

        if (user is null)
            throw new RecordNotFoundException(_configReader.GetKey(ConfigKeys.ExceptionRecordNotFound.ToString()),
                SharedErrorCode.RecordNotFound);

        var verification = _verificationService.IsRecentlyVerified(user.Id, changePasswordDto.MobileNumber, changePasswordDto.NotificationChannelType);
        
        if (verification is {IsVerified: false})
            throw new UserNotVerifiedException(_configReader.GetKey(ConfigKeys.ExceptionUserIsNotVerified.ToString()),
                SharedErrorCode.UserNotVerified);

        var newPassword = _generalUtility.HashPassword(changePasswordDto.Password,
            _configReader.GetKey(ConfigKeys.HashSalt.ToString()));

        if (user.Password == newPassword)
            throw new AuthenticationException(
                _configReader.GetKey(ConfigKeys.ExceptionNewPasswordIsSimilarOldOne.ToString()),
                SharedErrorCode.NewPasswordLikeOldOne);

        user.Password = newPassword;
        _unitOfWork.UserRepository.Update(user);
        _unitOfWork.Save();

        return true;
    }

Implementation of UserOfMobileQueryCriteria method:

protected virtual BaseQueryCriteria<UserEntity> UserOfMobileQueryCriteria(string mobileNumber)
{
    return new BaseQueryCriteria<UserEntity>(_ => _.MobileNumber == mobileNumber);
}

Implementation of GetFirstOne method:

public override T? GetFirstOne(IQuerySpecification<T> specification)
{
  return ApplySpecification(specification).FirstOrDefault();
}

private IQueryable<T> ApplySpecification(IQuerySpecification<T> spec)
{
    return GetQuery(_dbContext.Set<T>().AsQueryable(), spec);
}

private static IQueryable<T> GetQuery(IQueryable<T> queryable, IQuerySpecification<T> specification)
{
   var query = queryable;
   query = query.Where(specification.Filter);
          
   return query;
}

Test:

[Fact]
public void ResetPasswordReturnTrue()
{
 //Arrange
  var changePasswordDto = new ChangePasswordDto()
  {
    Code = "1234",
    Password = "stringst",
    PasswordConfirmation = "stringst",
    MobileNumber = "+111111111111",
    NotificationChannelType = NotificationChannelType.SMS
   };
        
   var user = new UserEntity
   {
     Id = Guid.NewGuid().ToString(),
     MobileNumber = changePasswordDto.MobileNumber,
     Role = UserRoles.Parent,
            FirstName = "Ahmad",
            LastName = "Ke"
        };

        Expression<Func<UserEntity, bool>> e = x => x.MobileNumber == changePasswordDto.MobileNumber;

        var userQueryBuilderMock = new Mock<BaseQueryCriteria<UserEntity>>(e);
        
        _userServiceMock.Protected()
            .Setup<BaseQueryCriteria<UserEntity>>("UserOfMobileQueryCriteria", changePasswordDto.MobileNumber)
            .Returns(  userQueryBuilderMock.Object );
        
       
        _userUnitOfWorkMock.Setup(r =>
                r.UserRepository.GetFirstOne(userQueryBuilderMock.Object))
            .Returns(user);
        
        var verification = new VerificationResponseDto
        {
            Code = changePasswordDto.Code,
            Id = Guid.NewGuid().ToString(),
            Recipient = user.MobileNumber,
            IsVerified = true,
            SendDate = DateTime.Today.AddMinutes(-10),
            UserId = user.Id,
            NotificationChannelType = changePasswordDto.NotificationChannelType
        };
        
  _verificationServiceMock.Setup(v => v.IsRecentlyVerified(user.Id, user.MobileNumber,NotificationChannelType.SMS)).Returns(verification);
        
  //Act
  var passwordChanged = _passwordService.ResetPassword(changePasswordDto);
        
  //Assert
  Assert.True(passwordChanged);
}
```    		

`IUserUnitOfWork` & `UserUnitOfWork`

public interface IUserUnitOfWork : IUnitOfWork { IRepository<UserEntity> UserRepository { get; } }

public class UserUnitOfWork : IUserUnitOfWork { private readonly UserDbContext _userContext; public IRepository<UserEntity> UserRepository { get; }

public UserUnitOfWork(UserDbContext userContext, IRepository<UserEntity> userRepository) { _userContext = userContext;

UserRepository = userRepository; }

    public void Save()
    {
        _userContext.SaveChanges();
    }

    public void Dispose()
    {
        _userContext.Dispose();
    }
}
    		
Result exception:

> Exceptions.RecordNotFoundException
>
> Exception of type 'Exceptions.RecordNotFoundException' was thrown.
>
> at Application.UserService.ResetPassword(ChangePasswordDto changePasswordDto) in Application\UserPasswordService.cs:line 23  
> at UnitTests.User.Application.UserServiceTest.ResetPasswordReturnTrue() in User\Application\UserServiceTest.cs:line 91

<!-- sl -->
[![Back this issue](https://raw.githubusercontent.com/devlooped/SponsorLink/main/docs/assets/img/separator.png "Back this issue")](https://www.devlooped.com/SponsorLink/github/issues/?s=devlooped)
[![Back this issue](https://img.shields.io/badge/backed-%240-EA4AAA?logo=githubsponsors "Back this issue")](https://www.devlooped.com/SponsorLink/github/issues/?s=devlooped)
<!-- sl -->

deva7mad avatar Jun 26 '22 08:06 deva7mad

Is this simply a usage question or do you believe this is a bug in Moq?

That repro code is rather non-minimal. Is there any chance you could trim that down any further, @deva7mad? If not, it would help if you could provide this as a VS project so we don't need to piece this together ourselves.

stakx avatar Jan 13 '23 16:01 stakx