KSF KSF - 10 months ago 65
C# Question

ApiController.Ok() returns null in unit tests

I have recently moved to a new project at my company. I have been tasked with writing unit tests for one of our controllers for a REST API. The controller implements the System.Web.Http.ApiController interface. I am using Moq, and it is my first time using this cool little library. My issue is that when I call

IHttpActionResult result = Ok();

in the controller, the call to Ok() returns null. This only happens when I am calling this method from the unit tests. If I call my method directly using Swagger, it works as expected.

I am thinking that possibly the Moq statements I'm using to setup the controller arguments/controller itself are messing with the instantiation of the controller somehow and making the inherited interface methods not accessible? Here is the code I'm using to set up the unit test:

//Mock ICrmService to return an Id for a created company
var crmCacheService = new Mock<ICrmCacheService>();
var crmService = new Mock<ICrmService>();

crmCacheService.Setup(x => x.CreateSubscription(It.IsAny<CreateSubscriptionModel>())).Returns(true);
crmService.Setup(x => x.CreateSubscription(It.IsAny<CreateSubscriptionModel>())).Returns(ReturnedSubscriptionId);

var controller = new Mock<SubscriptionController>(crmService.Object, crmCacheService.Object);
IHttpActionResult actionResult = controller.Object.CreateSubscriptions(Guid.NewGuid().ToString(), 1);

Does anyone have any idea why this would make the call to Ok() return null? And how I could set it up so that a call to Ok() will return the normal result of System.Web.Http.Results.OkResult?

Answer Source

Presumably your controller is your subject under test in this scenario, in which case you don't really want to mock it at all, but use a real instance instead (with mocked dependencies):

var controller = new SubscriptionController(crmService.Object, crmCacheService.Object);

As you are currently mocking it though and since the Ok() method is virtual in ApiController, Moq will be able to override it and will do so. Since the default mock behavior is to override everything to return defaults (MockBehavior.Loose) you will get a null return value.

If you do really want to mock it and return a value from Ok(), which again, I don't think you do, you can use Setup to have it pass through to the real method (among many other things, like specifying the return value you want to get):

controller.Setup(x => x.Ok()).CallBase();