1
votes

I'm using VisualStudio 2015, .NET 4.6, Moq 4.5.2, Nunit 3.4.1 to test a WebApi2 Controller. However, I am getting a null response object when mocking the intended controller method:

var response = actionResult as NegotiatedContentResult;

I am guessing I must be setting up my mock of the UserService incorrectly?

My suspicion is that this part is the culprit:

userServiceMock.Setup(service => service.InsertOrUpdateUser( It.IsAny())).Returns(1);

As I am getting the following in the output window:

'((System.Web.Http.Results.OkNegotiatedContentResult)actionResult).Request' threw an exception of type 'System.InvalidOperationException'

Is the problem that I am telling Moq to expect a return value of 1, but the Put method returns OkNegotiatedContentResult?

My questions are (possibly the same question):

1) Am I setting up my Moq correctly and

2) how do I resolve the problem so my response object is populated?

Thanks much.

Here is the Test method:

[Test]
public void Put_ShouldUpdate_User()
{
    // Arrange
    var userServiceMock = new Mock<IUserService>();

    userServiceMock.Setup(service => service.InsertOrUpdateUser(
        It.IsAny<User>())).Returns(1);

    var controller = new UsersController(userServiceMock.Object);

    // Act
    IHttpActionResult actionResult = controller.Put(

        new User()
        {
            Id = 1,
            Name = "Joe"
        });

    var response = actionResult as NegotiatedContentResult<User>;

    // Assert:
    Assert.IsNotNull(response);
    var newUser = response.Content;
    Assert.AreEqual(1, newUser.Id);
    Assert.AreEqual("Joe", newUser.Name);
}

Here is the UserController method:

// PUT api/users/1
public IHttpActionResult Put(User user)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    return Ok(_userService.InsertOrUpdateUser(user));

}

Finally, the method for the UserService:

public int InsertOrUpdateUser(User user)
{
    return _userRepository.InsertOrUpdateUser(user);
}
1
seems like the mock setup should be ok - the configured behavior should return an int like the method does. I did notice that your exception shows a cast to the OkNegotiatedContentResult<T> type, but your code attempts to access it as a NegotiatedContentResult<T> - these don't share a common base class, could that be the cause? - Andrew
Thanks Andrew. Yes, I caught that earlier and tried changing NegotiatedContentResult to OKNegotiatedContentResult but it didn't seem to matter so I just left it. - Peter
which line is the exception thrown on? - Andrew
Sorry, had to step through the code again. It is on this line: IHttpActionResult actionResult = controller.Put( etc... It seems to be generated in the UserControllers Put method. - Peter
Specifically, it looks like the error is being thrown inside UserController here: Ok(_tribeService.InsertOrUpdateTribe(tribe)); It seems to be thrown there. This is my call to my repository which I understand I do not need to be mocking in this test as Moq knows nothing about the Repository, it is just testing the Controller and the mocked Object I am passing to it? - Peter

1 Answers

1
votes

According to your code IUserService has

public interface IUserService {

    int InsertOrUpdateUser(User user);

}

which returns an int.

If you do the following in your controller

return Ok(_userService.InsertOrUpdateUser(user));

then based on the interface and your setup mock, that will return a response type of OkNegotiatedContentResult<int>. But in your test you do this

var response = actionResult as NegotiatedContentResult<User>;

where you cast your returned result as NegotiatedContentResult<User> this will cause Assert.IsNotNull(response); to fail as the cast will result in response being null.

Given the asserts of your test then you would have to update your controller's Put method to return the User user after the mocked action like so...

public IHttpActionResult Put(User user) {
    if (!ModelState.IsValid) {
        return BadRequest(ModelState);
    }
    var count = _userService.InsertOrUpdateUser(user);
    if(count == 1)
        return Ok(user);
    else
        return BadRequest(); // 500 (Internal Server Error) you choose. 
}

and also update the test as follows

//...other code removed for brevity

var response = actionResult as OkNegotiatedContentResult<User>;

// Assert:
Assert.IsNotNull(response);
var newUser = response.Content;
Assert.AreEqual(1, newUser.Id);
Assert.AreEqual("Joe", newUser.Name);