Xunit and Mock data with Moq

Viewed 39239

I'm new to unit testing, can anyone advise how to test public method (CreateUser) below using xUnit and Moq, thanks!

public async Task<bool> CreateUser(UserDTO newUser)
{
  newUser.CustomerId = _userResolverService.GetCustomerId();
  if (await CheckUserExists(newUser)) return false;
  var salt = GenerateSalt(10);
  var passwordHash = GenerateHash(newUser.Password, salt);

  await _usersRepository.AddAsync(new User()
  {
    Role = newUser.Role,
    CretedOn = DateTime.Now,
    CustomerId = newUser.CustomerId,
    Email = newUser.Email,
    FirstName = newUser.FirstName,
    LastName = newUser.LastName,
    PasswordHash = passwordHash,
    Salt = salt,
    UpdatedOn = DateTime.Now
  });

  return true;
}

Private methods below Check if user exists simply returns number of existing users

private async Task<bool> CheckUserExists(UserDTO user)
    {
      var users = await _usersRepository.GetAllAsync();
      var userCount = users.Count(u => u.Email == user.Email);
      return userCount > 0;
    }

Hash Generation

private static string GenerateHash(string input, string salt)
{
  var bytes = System.Text.Encoding.UTF8.GetBytes(input + salt);
  var sha256 = SHA256.Create();
  var hash = sha256.ComputeHash(bytes);

  return ByteArrayToString(hash);
}

Salt Generaion

private static string GenerateSalt(int size)
{
  var rng = RandomNumberGenerator.Create();
  var buff = new byte[size];
  rng.GetBytes(buff);
  return Convert.ToBase64String(buff);
}



private static string ByteArrayToString(byte[] ba)
{
  var hex = new StringBuilder(ba.Length * 2);
  foreach (byte b in ba)
    hex.AppendFormat("{0:x2}", b);
  return hex.ToString();
}

Thanks, J

EDIT 1

This is what I have so far:

    [Fact]
        public async void CreateUser_True()
        {
          //arrange
          var dataSource = new Mock<IRepository<User, int>>();
          var userResolverService = new Mock<IUserResolverService>();
          var tokenService = new Mock<ITokenService>();

      var users = new List<User>();
      users.Add(new User()
      {
        Email = "test@test.com",
        CustomerId = 1
      });
      dataSource.Setup(m => m.GetAllAsync()).ReturnsAsync(users); // Error Here with converting async task to IEnumerable...

          var accountService = new AccountService(dataSource.Object,userResolverService.Object,tokenService.Object);


          //act


          //assert


        }

Main problem is that I have no idea how to Mock, set up behavior of private Method "CheckUserExists", as it's calling IRepository which is mocked for class, so will this in this case return my mock setup for GetAllAsync from this private method?

EDIT 2

public async Task<TEntity> AddAsync(TEntity entity)
{
  if (entity == null)
  {
    throw new ArgumentNullException(nameof(entity));
  }
  _entities.Add(entity);
  await _context.SaveChangesAsync();
  return entity;
}

EDIT 3

[Fact]
public async void CreateUser_True()
{
  //arrange
  var users = new List<User>();
  users.Add(new User()
  {
    Email = "test@test.com",
    CustomerId = 1
  });
  _dataSource.Setup(m => m.GetAllAsync()).ReturnsAsync(users);
  _dataSource.Setup(m => m.AddAsync(It.IsAny<User>())).Returns<User>(Task.FromResult);
  var accountService = new AccountService(_dataSource.Object, _userResolverService.Object, _tokenService.Object);


  //act
  var result = await accountService.CreateUser(new UserDTO()
  {
    Email = "truetest@test.com"
  });

  var updatedUsersList = await _dataSource.Object.GetAllAsync();
  var usersCount = updatedUsersList.Count();

  //assert
  Assert.True(result);
  Assert.Equal(2, usersCount);

}
2 Answers

@sziszu As I understand correctly you can setup mock for unit test of your your public method. here is solution.

I have rewrite your code and my project structure is something look like visual studio project structure

Here is my code snippets

UserOperation Class

    public class UserOperation
    {
       #region Fields
    
       private readonly IUserResolverService _userResolverService;
       private readonly IUsersRepository _usersRepository;

       #endregion

       #region Constructor

       public UserOperation(IUserResolverService userResolverService, IUsersRepository usersRepository)
       {
          _userResolverService = userResolverService;
          _usersRepository = usersRepository;
       }

       #endregion

       #region Public Methods

       public async Task<bool> CreateUser(UserDTO newUser)
       {
          newUser.CustomerId = _userResolverService.GetCustomerId();
          if (await CheckUserExists(newUser)) return false;
          var salt = GenerateSalt(10);
          var passwordHash = GenerateHash(newUser.Password, salt);

          await _usersRepository.AddAsync(new User
          {
              Role = newUser.Role,
              CretedOn = DateTime.Now,
              CustomerId = newUser.CustomerId,
              Email = newUser.Email,
              FirstName = newUser.FirstName,
              LastName = newUser.LastName,
              PasswordHash = passwordHash,
              Salt = salt,
              UpdatedOn = DateTime.Now
          });

          return true;
       }

       #endregion

       #region PrivateMethods

       private async Task<bool> CheckUserExists(UserDTO user)
       {
           var users = await _usersRepository.GetAllAsync();
           var userCount = users.Count(u => u.Email == user.Email);
           return userCount > 0;
       }

       private static string GenerateHash(string input, string salt)
       {
           var bytes = Encoding.UTF8.GetBytes(input + salt);
           var sha256 = SHA256.Create();
           var hash = sha256.ComputeHash(bytes);

           return ByteArrayToString(hash);
       }

       private static string GenerateSalt(int size)
       {
           var rng = RandomNumberGenerator.Create();
           var buff = new byte[size];
           rng.GetBytes(buff);
           return Convert.ToBase64String(buff);
       }

       private static string ByteArrayToString(byte[] ba)
       {
           var hex = new StringBuilder(ba.Length * 2);
           foreach (byte b in ba)
           hex.AppendFormat("{0:x2}", b);
           return hex.ToString();
       }

       #endregion
   }

Below are my repositories and services.

   public interface IUsersRepository
   {
       Task AddAsync(User user);
       Task<ICollection<User>> GetAllAsync();
   }
   
   public class UsersRepository : IUsersRepository
   {
       public Task AddAsync(User user)
       {
            //Code for adding user
       }

       public Task<ICollection<User>> GetAllAsync()
       {
           //Code for get all user from DB
       }
   }

   public interface IUserResolverService
   {
       string GetCustomerId();
   }

   public class UserResolverService : IUserResolverService
   { 
       public string GetCustomerId()
       {
           //Code for Getting customerID.
       }
   }

Here You can test using mock

  public class UserOperationTest
  {
    private readonly UserOperation _sut;
    private readonly IUserResolverService _userResolverService;
    private readonly IUsersRepository _userRepository;

    public UserOperationTest()
    {
        _userResolverService = Substitute.For<IUserResolverService>();
        _userRepository = Substitute.For<IUsersRepository>();

        _sut = new UserOperation(_userResolverService, _userRepository);
    }

    [Fact]
    public void CreateUser_SuccessWithMock()
    {
        // Arrange
        var userDto = new UserDTO
        {
            Email = "ThirdUserUserEmail.Com"
        };

        var userList = new List<User>()
        {
            new User{CustomerId = "1", Email = "FirstUserEmail.Com"},
            new User{CustomerId = "2", Email = "SecondUserEmail.Com"}
        };
        _userResolverService.GetCustomerId().Returns("3");
        _userRepository.GetAllAsync().Returns(userList);
        _userRepository.When(x => x.AddAsync(Arg.Any<User>())).Do(x =>
        {
            userList.Add(new User {Email = userDto.Email, CustomerId = userDto.CustomerId});
        });
        
        //Act
        var result = _sut.CreateUser(userDto);

        // Assert
        result.Result.Should().BeTrue();
        _userResolverService.Received(1).GetCustomerId();
        _userRepository.Received(1).GetAllAsync();
        _userRepository.Received(1).AddAsync(Arg.Any<User>());
        userList.Count.Should().Be(3);
    }
}
Related