How to validate your .NET Core WebAPI model and return a 400 (BadRequest) response from your controller and test it with Moq
I know this is not a new question; however, if you search online, you will mostly find articles discussing the older .NET Framework. Here is my way of dealing with 400 (BadRequest) and 404 (NotFound) errors using the latest .NET Core 3.0 WebAPI methods.
Let us assume that you want to add some data annotations to a model of your API. For example, here we add a Range
:
public class PersonModel
{
[Range(0, 120)]
public int Age { get; set; }
}
You now validate the model in the controller method of your application, before you process with the business logic:
using Microsoft.AspNetCore.Mvc;
[Route("[controller]")]
public class PersonController : Controller
{
public async Task<ActionResult<PersonModel>> Save(
[FromBody] PersonModel model,
CancellationToken cancellationToken = default)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return await repository.SaveAsync(model, cancellationToken);
}
}
-
We use the
ModelState.IsValid
property to check for validation errors in the model. If this is the case, then we return aBadRequest
response from theMicrosoft.AspNetCore.Mvc.ControllerBase
abstract class, which is the parent class of theMicrosoft.AspNetCore.Mvc.Controller
class. -
We pass the
ModelState
object in the BadRequest-response, so that the caller of our service gets information about the properties that did not validate correctly. -
We always use as return-type the
ActionResult
for wrapping the response in it. This way we are flexible and we can return our model in a success case or another response when things go wrong. -
Check and use the other methods of ControllerBase for returning other Response-Codes like
NotFound
orNoContent
.
Test the IsValid code with XUnit and Moq
We now want to test the if-case with a unit-test. We will use the XUnit and the Moq libraries for that.
[Fact]
public async Task PersonController_WhenSaveIsCalled_ThenBadRequestIsReturned()
{
personController.ModelState.AddModelError("Age", "Range Error");
ActionResult<PersonModel> result =
await personController.Save(new PersonModel { Age = 200 }, default);
Assert.Equal(400, (result.Result as ObjectResult)?.StatusCode);
}
-
We first have to artificially add the errors in the ModeState object, since when we unit-test our code, the automatically-validation of .NET core does not run.
-
We then add a wrong value into the property that will get validated.
-
We call the controller method and Assert for an integer 400. We have to cast the
ActionResult.Result
object to aMicrosoft.AspNetCore.Mvc.ObjectResult
and then check itsStatusCode
property.
Thats it. Using the same principle you can also test for 404 responses.