Search code examples
c#asp.net-web-api.net-corexunit

Trouble with ActionResult in unit test can't Assert.Equal on POST return value (asp.net core mvc web api)


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.

debugger output


Solution

  • 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!!