Reputation: 383
I'm new to testing .net core web APIs and am struggling to test the POST method on my API. I can assert the type of the ActionResult that is returned, but I can't seem to figure out how to compare the result to the fixture that I'm creating.
I've debugged the unit test and seen that the CreatedAtAction call is returning an ActionResult. The top-level Value is null. However, the Value for the Result has the item.
I feel like I'm missing a cast or something and my code should be expecting a CreatedAtActionResult or something, but I can't seem to figure out how to get this to work.
Here is the (POST) method from my controller:
[HttpPost]
public async Task<ActionResult<TodoItem>> AddTodoItem(TodoItem item)
{
_context.TodoItems.Add(item);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}
Here is my unit test:
[Fact]
public async Task PostTodoItem_ShouldPass()
{
var fixture = new Fixture();
var item = fixture.Create<TodoItem>();
var result = await _controller.AddTodoItem(item);
Assert.IsType<ActionResult<TodoItem>>(result); // WORKS
Assert.Equal(item, result.Value); // DOESN'T WORK!!
}
I'm using Autofixture and the EF in-memory database for testing. All of this is getting set up in a shared database fixture class which runs before the unit test.
Here is my debugger output.
Upvotes: 3
Views: 7325
Reputation: 161
I wrapped @shrinith-sanil answer into an extension to make it easy to reuse with all my controllers. Thanks!
public static class ActionResultExtensions
{
/// <summary>
/// Becuase controllers return an ActionResult instead of ObjectResult, some fancy casting must be done.
/// https://stackoverflow.com/questions/57929044/trouble-with-actionresult-in-unit-test-cant-assert-equal-on-post-return-value
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="task"></param>
/// <returns></returns>
public static async Task<T> UnwrapAsync<T>(this Task<ActionResult<T>> task) where T : class
{
var result = await task;
var objectResult = result.Result as ObjectResult;
var value = objectResult.Value as T;
return value;
}
Upvotes: 0
Reputation: 211
Assert.Equal(item, result.Value); // DOESN'T WORK!!
Assert.Equal(item, ((ObjectResult)actual.Result).Value); // Try this it should work
Upvotes: 2
Reputation: 248
This appears to be an oddity in the object model here. I dont know the reason though. You call CreatedAtAction which returns a CreatedAtActionResult. That has a Value property which is an object. This is what you're passing the TodoItem into and you can see it in your debugger output.
This class does NOT inherit from ActionResult< T>, but thats what your function returns. ActionResult< T> DOES have an implicit operator that allows it any type to be cast to it and one that is for ActionResult objects (not generic).
So when the compiler sees you returning a CreatedAtActionResult, it needs to turn that into the ActionResult< T> so it calls the implicit cast from ActionResult (as CreatedAtActionResult : ObjectResult : ActionResult).
Therefore to get the Todo for the comparison you want you need to:
Assert.Equal(item, (result.Result as CreatedAtActionResult).Value); // SHOULD WORK!!
Upvotes: 8