Getting System.ArgumentException even while sending the right number of parameters while using Moq setup

369 Views Asked by At

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.

1

There are 1 best solutions below

0
On BEST ANSWER
.ReturnsAsync((TblUser user) =>

has only one parameter. This should be

.ReturnsAsync((TblUser user, string password) =>