devlooped / devlooped/moq

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

Open
#1,323 1 comment 0 reactions 1 assignee View on GitHub

@deva7mad is already working on this.

Since Jan 13, 2023.

needs-repro
Dominant language
C#
Stars
6.4k
Forks
835
PR merge metrics
No merged PRs in 30d

Description

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

Back this issue
Back this issue

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.