Search code examples
c#unit-testing.net-coremoqmstest

MSTest Unit Testing Controller


Learning MSTest -- trying to unit test a REST function: AccountController (which I believe will work) I want to start with Register. I can't get the Mocks setup for some reason. (Moq is installed). Also, I'm confused as to how this could possibly be of any use; if I mock all the controller inputs, I'm not going to get anything back? Your Advice?

AccountController - target of the unit test:

namespace API.Controllers
{
    public class AccountController : BaseApiController
    {
        private readonly UserManager<AppUser> _userManager;
        private readonly SignInManager<AppUser> _signInManager;
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly ITokenService _tokenService;
        private readonly IMapper _mapper;
        public AccountController(
            UserManager<AppUser> userManager,
            SignInManager<AppUser> signInManager,
            RoleManager<IdentityRole> roleManager,
            ITokenService tokenService,
            IMapper mapper)
        {
            _signInManager = signInManager;
            _roleManager = roleManager;
            _mapper = mapper;
            _tokenService = tokenService;
            _userManager = userManager;
        }

    [HttpPost("register")]
    // [Authorize]
    [Authorize(Roles = "Admin")]
    public async Task<ActionResult<UserDto>> Register(RegisterDto registerDto)
    {
        if (await this.UserExistsAsync(registerDto.Username)) return BadRequest("Username is taken");

        var user = _mapper.Map<AppUser>(registerDto);
        user.UserName = registerDto.Username.ToLower();
        var result = await _userManager.CreateAsync(user, registerDto.Password);

        if (!result.Succeeded) return BadRequest(result.Errors);

        var roleResult = await _userManager.AddToRoleAsync(user, "User");
        if (!roleResult.Succeeded) return BadRequest(result.Errors);

        return new UserDto
        {
            Username = user.UserName,
            Token = await _tokenService.CreateToken(user),
            KnownAs = user.KnownAs
        };
    }

BTW BaseController is simply:

{
    [ApiController]
    [Route("api/[controller]")]
    public class BaseApiController : ControllerBase
    {      
    }
}

The Unit Test so far is:

namespace API.Controllers.Tests
{
    [TestClass()]
    public class AccountControllerTests
    {
        [TestMethod()]
        public async void AccountController_Register()
        {
            var _userManager = new Mock<UserManager<AppUser>>();
            var _signInManager = new Mock<SignInManager<AppUser>>();
            var _roleManager = new Mock<RoleManager<IdentityRole>>();
            ITokenService tokenService;
            IMapper mapper;

            RegisterDto userDto = new RegisterDto()
            {
                Username = "Admin",
                Password = "Secret1234",
                FirstName = "Johnny",
                LastName = "Appleseed",
                Phone1 = "6175551212",
                Phone2 = "",
                KnownAs = "Dude",
                EmailAddress = "Dude@Somewhere.com",
                Question = "ask",
                Answer = "answer",
            };

            AccountController accountController = new AccountController(_userManager, 
                                                    _signInManager, 
                                                    _roleManager, 
                                                    tokenService, 
                                                    mapper);
            await accountController.Register(userDto);

            Assert.Fail(); <<---- this will be replaced obviously
        }

The compiler error looks like this: enter image description here

Other arguments have a similar error message.

Your Advice OR point me to a tutorial somewhere that covers MSTest Mocks :-) Thanks in advance.

Chuck


Solution

  • For Mock<T> you need to call .Object to get the mocked type

    AccountController accountController = 
        new AccountController(_userManager.Object, //<-- NOTE ".Object" on Mock<T>
            _signInManager.Object, 
            _roleManager.Object, 
            tokenService, 
            mapper);
    

    Also you will need to setup the expected behavior using .Setup so that the mock behave as expected when the subject under test is being exercised.

    Reference Moq Quickstart to get an idea of how to actually use the library