-2

It's been a whole day and I can't able to find a solution. My Controller's ActionMethod is working fine when it calls from PostMan. But when I call it from my Unit Test Method, it keeps returning null.

Here is my code. I already found an answer on StackOverflow: https://stackoverflow.com/a/56498657/11425180
But this is not resolving my issue.

Here is my ActionMethod

[HttpPost("register")]
        public async Task<IActionResult> RegisterUser([FromBody] CreateUserRequest request)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);
            try
            {
                //  Map request with dto and give to service
                CreateUserRequestDto createDto = _mapper.Map<CreateUserRequestDto>(request);
                CreateUserResponseDto response = await _userService.CreateUser(createDto);

                if (response.IsSuccess)
                {
                    Success success = new Success(message: SuccessMessages.UserCreated, data: response);
                    return Ok(success);
                }

                Error error = new Error(message: ErrorMessages.UserRegistrationFailed, description: response.Error.Description);
                return BadRequest(error);
            }
            catch (Exception ex)
            {
                return HandleException(ex);
            }
        }

Here is my test Class

    public class MockAccountControllerTests
    {
        readonly UserController _accountController;

        public MockAccountControllerTests()
        {
            Mock<IMapper> _mockMapper = new Mock<IMapper>();
            Mockers.InitializeMappers(_mockMapper);
            UserManager<AppUser> _mockUserManager = Mockers.MockUserManager<AppUser>().Object;
            UserRepository _mockUserRepository = new Mock<UserRepository>(_mockUserManager, _mockMapper.Object).Object;
            UserService _mockUserService = new Mock<UserService>(_mockUserRepository).Object;

            _accountController = new Mock<UserController>(_mockUserService, _mockMapper.Object).Object;
        }

        [Fact]
        public async Task RegisterUser_NullUserNamePassword_ThrowsException()
        {
            CreateUserRequest request = RequestHelpers.CreateUserRequest(null, null);

            IActionResult result = await _accountController.RegisterUser(request);

            ObjectResult badRequest = result as ObjectResult;

            Assert.NotNull(badRequest);
            Assert.True(badRequest is BadRequestObjectResult);
            Assert.Equal(StatusCodes.Status400BadRequest, badRequest.StatusCode);
            Assert.NotNull(badRequest.Value);
            Assert.IsType<Error>(badRequest.Value);
        }
    }

At this line in my Test Method

var result = await _accountController.RegisterUser(request);

The result is null

I also tried to assign a value of BadRequest to a variable like this

var a = BadRequest("Test Message"); The a is also null in this case.

How this will be corrected? Am I doing something wrong in my ActionMethod or TestMethod? Kindly review.

Arslan Munir
  • 327
  • 4
  • 8
  • 1
    Read [ask] and provide a [mre]. How do you initialize your `_accountController`? What does `HandleException()` return? Have you tried debugging your test and stepping through your code? – CodeCaster Sep 10 '19 at 13:19
  • What is the value of `result.GetType` (check in the `Immediate Window`)? – mjwills Sep 10 '19 at 13:22
  • 1
    Listen buddy, if `await _accountController.RegisterUser(request)` returns `null`, then the problem is in your code. In a unit test, a lot of the ASP.NET plumbing is simply not running, so it is not comparable to issuing a request with PostMan to your running API. So again: show how you initialize `_accountController`, and step through its code to see where `null` is returned. I'm not being clever, I'm telling you from years of experience of building and testing in ASP.NET (MVC (Core)) that the problem is in your code. – CodeCaster Sep 10 '19 at 14:47
  • @CodeCaster If you thing that there is a problem in my _accountController, I am simply Mocking AccountController – Arslan Munir Sep 10 '19 at 14:49
  • 2
    I wouldn't be surprised, for example, if your `_accountController` is initialized by a `new Mock().Object`, because `BadRequest()` will never return `null`. Testing a mock is the wrong approach, you mock dependencies, not the system under test. So stop fighting with me in comments, and [edit] your question show your entire test class. – CodeCaster Sep 10 '19 at 14:50
  • 2
    @ArslanMunir the moral of this is that if you do not show us the whole picture in a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example), then you end up wasting the time of those volunteering to help you solve **your** problem, with needless back and forth. – Nkosi Sep 10 '19 at 15:04

2 Answers2

3

This line is the culprit:

 _accountController = new Mock<UserController>(_mockUserService, _mockMapper.Object).Object;

Don't mock your system under test, because then you're not testing your code, but simply whether Moq does what it's supposed to do. And it does: it returns default values (null for reference types) for method calls that aren't SetUp().

So instead initialize it to an actual instance of your controller:

 _accountController = new UserController(_mockUserService, _mockMapper.Object);

See also: Test controller logic in ASP.NET Core on docs.microsoft.com.

CodeCaster
  • 131,656
  • 19
  • 190
  • 236
-1

The only possible explanation is that whatever the type of result, it's not derived from ObjectResult, so casting it to ObjectResult via as results in a null value being returned.

The thing is that, I don't see any output that returns something that doesn't derive from ObjectResult, except in your catch block where you're returning HandleException(ex). It's not clear what the type returned is here, so I can only assume that that's where your problem lies. In short, that needs to return an ObjectResult type. Things like BadRequestResult without Object in their names derive from StatusCodeResult, which shares no lineage with ObjectResult.

It's also worth mentioning that once you get past this assertion, your next will fail. Since you're casting whatever the result is to ObjectResult and saving it to badRequest, the assertion Assert.True(badRequest is BadRequestObjectResult) will always fail, because it will always be ObjectResult, not BadRequestObjectResult.

Chris Pratt
  • 207,690
  • 31
  • 326
  • 382