To test the validation of data models for REST API Controllers, just creating an instance of the controller class and calling the intended method is not an option. It simply does not test the validation, it skips the validation. We need a real REST Call but we need it from our xUnit test, not a manual call when starting the application manually. Two packages (Refit and IntegrationFixture) can help us with that and this article explains how.
Introduction
With xUnit, it is really easy to call a method and validate the response. However, a method call does not always reflect what is really happening. For example, when doing a REST API call to trigger a controller method, some validation is likely to be done first. This validation may not be part of your application itself but set by validation attributes such as [Required]
. This article explains how to solve that problem easily. You do not need so much boilerplate code, just the right packages and tricks.
Background
If you have some experience with xUnit and REST API development for .NET Core, it will help you understand the explanation given here.
Using the Code
Here is the method we want to test. It returns the full name based on the first name and last name. The middle name can be included too, but is optional.
[HttpPost]
public ActionResult<string> Post([FromBody] Name value)
{
return Ok($"{value.FirstName} {value.MiddleName} {value.LastName}");
}
Logically, the data model needs to be implemented too.
public class Name
{
[Required]
public string FirstName { get; set; }
public string MiddleName { get; set; }
[Required]
public string LastName { get; set; }
}
As it becomes clear from the code shown above, the first name and last name are required. The middle name is not.
We cannot just call the controller method from a controller instance. This would skip the validation triggered by the validation attributes. However, we can manually test the application by starting it and doing a REST Call. Logically, that is hard to implement with xUnit and hard to make part of the CI build. Here is the solution:
At first, we need a Refit interface to do a rest call when making the application self-hosting part of our test. Refit is available as a NuGet package. This how the interface looks like:
public interface ILogicClient
{
[Post("/api/logic")]
Task<ApiResponse<string>> Post([Body] Name name);
}
Now we need to create an implementation and instance of it programmatically in our test. To do that, we need a new NuGet package which enables the self hosting too. This package (IntegrationFixture
) is also available as a NuGet package.
[Theory]
[InlineData("Boris","Alexander", "Johnson",
HttpStatusCode.OK, "Boris", "Alexander", "Johnson")]
[InlineData("Boris", "Alexander", null, HttpStatusCode.BadRequest)]
[InlineData(null, "Alexander", "Johnson", HttpStatusCode.BadRequest)]
[InlineData("Boris", "Alexander", "", HttpStatusCode.BadRequest)]
[InlineData("", "Alexander", "Johnson", HttpStatusCode.BadRequest)]
public async Task NamesTest(string firstName, string middleName,
string lastName, HttpStatusCode expectedStatusCode, params string[] responseContains)
{
using (var fixture =
new RefitFixture<Startup, ILogicClient>(RestService.For<ILogicClient>))
{
var refitClient = fixture.GetRefitClient();
var response = await refitClient.Post(new Name
{
FirstName = firstName,
MiddleName = middleName,
LastName = lastName,
});
var statusCode = response.StatusCode;
Assert.Equal(expectedStatusCode, statusCode);
var content = response.Content;
foreach (var expectedResponse in responseContains)
{
Assert.Contains(expectedResponse, content);
}
}
}
As it becomes clear from the code above, we have several test cases. Depending on which names are available, a certain status code is returned. This is what the validation attributes are supposed to trigger so this is what we assert on. Moreover, we also assert the content of the response when having a positive result. The problem is solved now. The RefitFixture
is crucial here. It knows the Startup
class to enable the self-hosting of the application. Moreover, it knows the refit interface ILogicClient
too to do REST calls to the self-hosting application. This solves our problem: we can test the validation attributes with xUnit instead of having a need to start the application. The code in this article is available on GitHub.
Points of Interest
For me, the RefitFixture
class from the mentioned NuGet package is really helpful. When I just started developing .NET Core applications, I executed my applications manually to test the validation. This is not needed anymore. Validations and other middleware like things can be tested with xUnit, just like regular unit tests.
History
- 21st May, 2020: Initial version