Today I had the opportunity to use the new MVC testing framework for integration tests. In a previous post, I wrote about CodedUI and now I want to show a different approach.
When using CodedUI I stumbled over some problems, which could become a bit of burden over the time:
- Test setup: it is only possible to write the tests for the UI against a running application
- running Tests: When running the tests on the local machine, CodedUI takes over the controls and it is not possible to work.
- Continuous Integration: CodedUI will not run out of the box, because it needs controls over the mouse and access to a browser
The Testingframework lets you setup a testserver in your code. Instantiate the testserver in your SetUp method and for the generic type use your startup.cs.
[TestFixture]
public class BasicTests
{
private WebApplicationFactory<Startup> _factory;
[SetUp]
public void Setup()
{
_factory = new WebApplicationFactory<Startup>();
}
}
The next step is to create a client in your test and send a request. In my case I just want to test, that the root page works:
[Test]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(“/”);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.AreEqual(“text/html; charset=utf-8”,
response.Content.Headers.ContentType.ToString());
}
The good part here is, that the Tests will run without an UI and just return the response from the web server.
When it is required to customize our setup from our web server, we can inherit from WebApplicationFactory and override the ConfigureWebHost function. The following configuration will setup an in memory database for the EntityFrameworkCore, seed the database and disable the “Authorize” filter. It is not required to test the ASP.NET Authentication(this is already done by microsoft), I would like to test my own code:
public class NoAuthenticationWebApplicationFactory<TStartup>
: WebApplicationFactory<Startup>
{
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<WorkshopContext>(options =>
{
options.UseInMemoryDatabase(“InMemoryDbForTesting”);
options.UseInternalServiceProvider(serviceProvider);
});
services.AddMvc(opts => { opts.Filters.Add(new AllowAnonymousFilter()); });
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
// context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<WorkshopContext>();
var logger = scopedServices
.GetRequiredService<ILogger<NoAuthenticationWebApplicationFactory<TStartup>>>();
// Ensure the database is created.
db.Database.EnsureCreated();
try
{
// Seed the database with test data.
Utilities.InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, $”An error occurred seeding the ” +
“database with test messages. Error: {ex.Message}”);
}
}
});
}
}
With that it is possible to customize your web server and run tests against it. Just use the new WebApplicationFactory in your tests. With that setup we can:
- Customize our web server for each test
- can run the tests at all time without having to stop working.
In my next post I’ll write about how you can parse the webpages and navigate through them! Feel free to share your ideas about integration testing!