Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing controller methods which return IActionResult

I'm in the process of building an ASP.NET Core WebAPI and I'm attempting to write unit tests for the controllers. Most examples I've found are from the older WebAPI/WebAPI2 platforms and don't seem to correlate with the new Core controllers.

My controller methods are returning IActionResults. However, the IActionResult object only has a ExecuteResultAsync() method which requires a controller context. I'm instantiating the controller manually, so the controller context in this instance is null, which causes an exception when calling ExecuteResultAsync. Essentially this is leading me down a very hacky path to get these unit tests to successfully complete and is very messy. I'm left wondering that there must be a more simple/correct way of testing API controllers.

Also, my controllers are NOT using async/await if that makes a difference.

Simple example of what I'm trying to achieve:

Controller method:

[HttpGet(Name = "GetOrdersRoute")] public IActionResult GetOrders([FromQuery]int page = 0) {      try      {         var query = _repository.GetAll().ToList();          int totalCount = query.Count;         int totalPages = (int)Math.Ceiling((double)totalCount / pageSize) - 1;         var orders = query.Skip(pageSize * page).Take(pageSize);          return Ok(new         {            TotalCount = totalCount,            TotalPages = totalPages,             Orders = orders         });      }      catch (Exception ex)      {         return BadRequest(ex);      } } 

Unit test:

[Fact] public void GetOrders_WithOrdersInRepo_ReturnsOk() {      // arrange      var controller = new OrdersController(new MockRepository());       // act      IActionResult result = controller.GetOrders();       // assert      Assert.Equal(HttpStatusCode.OK, ????); } 
like image 469
Jake Shakesworth Avatar asked Dec 22 '16 23:12

Jake Shakesworth


People also ask

Should I return IActionResult or ActionResult?

The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. The ActionResult types represent various HTTP status codes. Any non-abstract class deriving from ActionResult qualifies as a valid return type.

Does it make sense to unit test controllers?

Yeah I think it does. The unit tests will test individual functionality and also provide you with a means to test if the unit was broken when other code changed. The integration test then tests the end-to-end process.

How do I create a controller unit test?

First we need to add an employee class in the Models folder. Following is the Employee class implementation. We need to add EmployeeController. Right-click on the controller folder in the solution explorer and select Add → Controller.


2 Answers

Assuming something like the

public IActionResult GetOrders() {     var orders = repository.All();     return Ok(orders); } 

the controller in this case is returning an OkObjectResult class.

Cast the result to the type of what you are returning in the method and perform your assert on that

[Fact] public void GetOrders_WithOrdersInRepo_ReturnsOk() {     // arrange     var controller = new OrdersController(new MockRepository());      // act     var result = controller.GetOrders();     var okResult = result as OkObjectResult;      // assert     Assert.IsNotNull(okResult);     Assert.AreEqual(200, okResult.StatusCode); } 
like image 74
Nkosi Avatar answered Sep 20 '22 15:09

Nkosi


You can also do cool things like:

    var result = await controller.GetOrders();//     var okResult = result as ObjectResult;      // assert     Assert.NotNull(okResult);     Assert.True(okResult is OkObjectResult);     Assert.IsType<TheTypeYouAreExpecting>(okResult.Value);     Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); 

Thanks

like image 20
Ernest Avatar answered Sep 17 '22 15:09

Ernest