ApiTester (package UnitTestEx, plus your test framework package) wraps ASP.NET Core's WebApplicationFactory to host your API in-process. Requests go through the full ASP.NET Core pipeline: middleware, routing, model binding, filters, authorization and serialization. External dependencies are replaced through DI.
using var test = ApiTester.Create<Startup>(); // Or ApiTester.Create<Program>() for minimal hosting.Where the API's appsettings.json isn't next to the test assembly, use UseSolutionRelativeContentRoot("tests/My.Api"). Example: Success2.
Controller<TController>().Run(c => c.Method(args)) takes the method call, works out the HTTP method, route, query string and body from the controller's attributes, and sends a real HTTP request through TestServer. This gives refactor-safe, strongly-typed tests that still exercise the whole pipeline.
test.Controller<PersonController>()
.Run(c => c.Get(1))
.AssertOK()
.AssertValue(new Person { Id = 1, FirstName = "Bob", LastName = "Smith" });Example: Get_Test1, Get_Test4 (not found), Update_Test1, GetByArgs_Test1.
Http() sends an explicit request. Http<TResponse>() also gives the response a type, so ExpectValue and AssertValue are strongly typed.
test.Http()
.Run(HttpMethod.Post, "Person/1", new Person { FirstName = "Bob", LastName = "Smith" })
.AssertOK();
test.Http<Person>()
.ExpectStatusCode(HttpStatusCode.OK)
.ExpectValue(new Person { Id = 1, FirstName = "Bob", LastName = "Smith" })
.Run(HttpMethod.Post, "Person/1", new Person { FirstName = "Bob", LastName = "Smith" });Example: Http_Get1, Http_Update1, Http_Update_Http2, Http_Update_Http3_AsJson.
AssertErrors understands both ValidationProblemDetails and the simple { field: [messages] } error shape. ApiError also converts implicitly from a (field, message) tuple.
test.Http().Run(HttpMethod.Get, "product/test/problem").AssertErrors(new ApiError("id", "Not specified."));
test.Http<Person>().Run(HttpMethod.Post, "Person/88", p).Assert(HttpStatusCode.BadRequest, "No can do eighty-eight.");Example: ValidationProblemDetails, Http_Update_Http5, Update_Test6.
To check an IActionResult created outside the pipeline, wrap it in a ValueAssertor. Then call ToActionResultAssertor(), or ToHttpResponseMessageAssertor(httpRequest) to run the result's ExecuteResultAsync and assert the HTTP response it produces. Example: Type_IActionResult.
| Method | Description |
|---|---|
UseUser(userName) / UseUser(object) |
Sets the test user name for all requests; it defaults to TestSetUp.DefaultUserName ("Anonymous"). UnitTestEx doesn't create claims or tokens itself. The user name is passed to TestSetUp.OnBeforeHttpRequestMessageSendAsync, where you add whatever your API's authentication expects, such as a bearer token or header. |
Http().WithUser(...) / Controller<T>().WithUser(...) |
Sets the user for a single tester. |
BypassAuthorization() |
Replaces IAuthorizationHandler with a handler that succeeds every requirement. Use it when a test isn't about authorization. |
AddPreRunAction, AddPostRunBeforeExpectationsAction, AddPostRunAfterExpectationsAction and AddPostRunAction register logic that runs for every Run on that tester. Use them for things like adding a standard expectation, without repeating it in each test.
test.Services (the built IServiceProvider), test.Configuration, test.GetTestServer() and test.GetLogger<T>() give direct access for any arrangement or assertion the fluent API doesn't cover. Example: ServiceProvider, Configuration.
See also: Dependency injection and configuration, Assertions and expectations, HTTP client mocking.