Sometimes unit tests just don’t cut it. This is where integration tests come in. This however brings a whole new set of issues with finding the beast way to isolate the aspects under test and mock everything else away.
Problem statement
Suppose, we’ve got an api and a test that needs to make an http call to our api endpoint, like so:
[ApiController]
public class TestController : ControllerBase {
public IActionResult OkTest() {
return Ok(true);
}
}
.....
public class TestControllerTests {
private readonly HttpClient _client;
public TestControllerTests() {
_client = TestSetup.GetTestClient();
}
[Test]
public async Task OkTest() {
var path = GetPathHere(nameof(OkTest)); // should return "/api/test/oktest".
var response = await _client.GetAsync(path);
response.EnsureSuccessStatusCode();
}
}
Solution
Knowing that ASP.NET Core comes with such a lightweight package now, and exposes so many extensibility points, one approach we found efficient was to build up the whole Host
and query its properties:
private string GetPathHere(string actionName)
{
var host = Program.CreateWebHostBuilder(new string[] { }).Build();
host.Start();
IActionDescriptorCollectionProvider provider = (host.Services as ServiceProvider).GetService<IActionDescriptorCollectionProvider>();
return provider.ActionDescriptors.Items.First(i => (i as ControllerActionDescriptor)?.ActionName == actionName).AttributeRouteInfo.Template;
}
[TestMethod]
public void OkTestShouldBeFine()
{
var path = GetPathHere(nameof(ValuesController.OkTest)); // "api/test/oktest"
}
Applicability
This is pretty basic case we’ve been dealing with, and the code makes quite a few assumptions. This approach however seems to hold up pretty well and surely will be our starting point next time round we test MVC actions!