While unit testing my ASP.NET web api controllers,for one of my post requests, I am sending two arguments in the repo function, Like usual I am mocking the repository setup, but even when I send two arguments, I get the argument exception.
Repo Code-
public async Task<TblUser> Register(TblUser user, string password)
{
byte[] passwordHash, passwordSalt;
CreatePasswordHash(password, out passwordHash, out passwordSalt);
user.APasswordHash = passwordHash;
user.APasswordSalt = passwordSalt;
await _context.TblUser.AddAsync(user);
await _context.SaveChangesAsync();
return user;
}
Controller code-
[HttpPost("register")]
public async Task<IActionResult> Register(UserForRegisterDto userForRegisterDto)
{
userForRegisterDto.AUsername = userForRegisterDto.AUsername.ToLower();
if (await _repo.UserExists(userForRegisterDto.AUsername))
return BadRequest("username already exists");
if(!ModelState.IsValid || userForRegisterDto.AUsername == null || userForRegisterDto.Password == null || userForRegisterDto.AEmail ==null || userForRegisterDto.Aname ==null)
{
return BadRequest("invalid user");
}
var userToCreate = _mapper.Map<TblUser>(userForRegisterDto);
var createdUser = await _repo.Register(userToCreate, userForRegisterDto.Password);
var userToReturn = _mapper.Map<UserForDetailedDto>(createdUser);
return CreatedAtRoute("GetUser", new
{
controller = "Users",
id = createdUser.ACustomerId
}, userToReturn
);
}
Controller Test code-
[Test]
public async Task GivenAValidUser_WhenIRegisterANewUser_ThenItReturnsOkWithResponse()
{
_mockAuthRepository = new Mock<IAuthRepository>();
_mockAuthMapper = new Mock<IMapper>();
_mockConfig = new Mock<IConfiguration>();
UserForRegisterDto expectedUser = new UserForRegisterDto
{
Aname = "Luffy",
AUsername = "luffy",
AEmail = "[email protected]",
Password = "password",
ADob = new DateTime(2000, 12, 12)
};
_mockAuthMapper.Setup(mapper => mapper.Map<TblUser>(It.IsAny<UserForRegisterDto>()))
.Returns(new TblUser());
_mockAuthMapper.Setup(mapper => mapper.Map<UserForDetailedDto>(It.IsAny<TblUser>()))
.Returns(new UserForDetailedDto());
_mockAuthRepository.Setup(repo => repo.Register(It.IsAny<TblUser>(), It.IsAny<string>()))
.ReturnsAsync((TblUser user) => user);
_authController = new AuthController(_mockAuthRepository.Object,_mockConfig.Object, _mockAuthMapper.Object);
var tblUser = await _authController.Register(expectedUser);
var okResult = tblUser as OkObjectResult;
Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(okResult);
Assert.IsAssignableFrom<OkObjectResult>(tblUser);
var result = ((OkObjectResult)tblUser).Value;
Assert.NotNull(result);
Assert.AreEqual(expectedUser, result);
Assert.IsAssignableFrom<TblUser>(result);
}
The exception is thrown by the line-
_mockAuthRepository.Setup(repo => repo.Register(It.IsAny<TblUser>(), It.IsAny<string>()))
.ReturnsAsync((TblUser user) => user);
Like you can see, while setting up the mock repository, I am sending two arguments but I get the error-
Message:
System.ArgumentException : Invalid callback. Setup on method with 2 parameter(s) cannot invoke callback with different number of parameters (1).
Stack Trace:
<>c__DisplayClass22_0
<SetReturnComputedValueBehavior>g__ValidateCallback|4(Delegate callback)
MethodCall.SetReturnComputedValueBehavior(Delegate valueFactory)
NonVoidSetupPhrase`2.Returns[T1](Func`2 valueExpression)
GeneratedReturnsExtensions.ReturnsAsync[T,TMock,TResult]
(IReturns`2 mock, Func`2 valueFunction) AuthControllerTests.GivenAValidUser_WhenIRegisterANewUser_ThenItReturnsOkWithResponse() line 43
GenericAdapter`1.GetResult()
AsyncToSyncAdapter.Await(Func`1 invoke)
TestMethodCommand.RunTestMethod(TestExecutionContext context)
TestMethodCommand.Execute(TestExecutionContext context)
SimpleWorkItem.PerformWork()
I am new to unit testing but so far, my unit tests for GET and POST requests have been working fine this way. Is this an issue Hashing function I am calling inside?
Controller code that was working- [AllowAnonymous] [HttpPost()] public async Task AddMovie(MovieForDetailedDto movieForDetailedDto) { if (await _repo.MovieExists(movieForDetailedDto.ATitle)) return BadRequest("movie already exists");
else if(!ModelState.IsValid || movieForDetailedDto.ATitle == null || movieForDetailedDto.APrice == null || movieForDetailedDto.AMovieDescription ==null)
{
return BadRequest("movie details not valid");
}
var movieToCreate = _mapper.Map<TblMovie>(movieForDetailedDto);
var createdMovie = await _repo.AddMovie(movieToCreate);
return Ok(createdMovie);
}
Test that worked for the above controller-
[Test]
public async Task GivenAValidMovie_WhenIPostANewMovie_ThenItReturnsOkWithResponse()
{
_mockMovieRepository = new Mock<IMovieRepository>();
_mockMovieMapper = new Mock<IMapper>();
TblMovie expectedMovie = new TblMovie
{
AMovieId = 55,
ATitle = "redemption",
AMovieDescription = "An action comedy adventure about brilliant robotics prodigy Hiro Hamada, who finds himself in the grips of a criminal plot that threatens to destroy the fast-paced, high-tech city of San Fransokyo. With the help of his closest companion-a robot named Baymax-Hiro joins forces with a reluctant team of first-time crime fighters on a mission to save their city.",
ADuration = "105 min",
APrice = "10",
APurchasePrice = "25",
ARating = 5,
AImageLink = "http://upload.wikimedia.org/wikipedia/en/4/4b/Big_Hero_6_%28film%29_poster.jpg",
ATrailerLink = "//www.youtube.com/embed/z3biFxZIJOQ",
AGenre = "Comedy",
AWideImage = "https://github.com/tushar23091998/MovieRentalApp-FrontEnd/blob/master/src/app/images/bighero6.jpg?raw=true"
};
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>()))
.Returns(expectedMovie);
_mockMovieRepository.Setup(repo => repo.AddMovie(It.IsAny<TblMovie>()))
.ReturnsAsync((TblMovie movie) => movie);
_moviesController = new MoviesController(_mockMovieRepository.Object, _mockMovieMapper.Object);
var tblMovie = await _moviesController.AddMovie(new MovieForDetailedDto
{
AMovieId = 55,
ATitle = "redemption",
AMovieDescription = "An action comedy adventure about brilliant robotics prodigy Hiro Hamada, who finds himself in the grips of a criminal plot that threatens to destroy the fast-paced, high-tech city of San Fransokyo. With the help of his closest companion-a robot named Baymax-Hiro joins forces with a reluctant team of first-time crime fighters on a mission to save their city.",
ADuration = "105 min",
APrice = "10",
APurchasePrice = "25",
ARating = 5,
AImageLink = "http://upload.wikimedia.org/wikipedia/en/4/4b/Big_Hero_6_%28film%29_poster.jpg",
ATrailerLink = "//www.youtube.com/embed/z3biFxZIJOQ",
AGenre = "Comedy",
AWideImage = "https://github.com/tushar23091998/MovieRentalApp-FrontEnd/blob/master/src/app/images/bighero6.jpg?raw=true"
});
var okResult = tblMovie as OkObjectResult;
Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(okResult);
Assert.IsAssignableFrom<OkObjectResult>(tblMovie);
var result = ((OkObjectResult)tblMovie).Value;
Assert.NotNull(result);
Assert.AreEqual(expectedMovie,result);
Assert.IsAssignableFrom<TblMovie>(result);
}
So my previous repo functions which took only one parameter were working fine.
Thanks for your time. Apologies if the answer is too obvious.
.ReturnsAsync((TblUser user) =>
has only one parameter. This should be
.ReturnsAsync((TblUser user, string password) =>