Within the .NET community, integration testing has become a common practice thanks to advancements in the framework. ASP.NET Core has a robust integration testing suite allowing .NET developers to run in-memory versions of their web applications, with the ability to issue requests and verify the responses.
In general, the test suite is fine, but it does have its limits, mainly that responses are the literal payloads and not executed within the context of browser session. In this post, we’ll see how we can use the Playwright library in combination with XUnit to test our web applications as users might.
What Is Playwright?
Playwright is a library that enables developers to write end-to-end tests for their web applications. Developers using Playwright can automate popular browser engines Chromium, Firefox, and Webkit across all modern operating systems: Linux, macOS, and Windows. With a selection of web engines, developers can test simple HTML pages to complex single-page apps with no limits. Playwright also has excellent support regardless of a developer’s technology of choice, with APIs written for JavaScript, Typescript, Python, Java, and C#. Tests will also run during CI/CD builds, helping ensure UX bugs don’t work themselves into production.
While Playwright’s primary purpose is for end-to-end tests, developers can also use it to scrape data from existing web applications. Please use this knowledge respectfully and responsibly.
How To Use Playwright With ASP.NET Core and XUnit
First, we’ll need to install XUnit
, Microsoft.NET.Test.Sdk
, and PlaywrightSharp
NuGet packages.
Sadly, we can’t use the integration library that is part of ASP.NET Core. As mentioned previously, it only runs in-memory, which means a browser can’t navigate to any endpoints we expose. The problem requires some ingenuity on our part. Luckily, we can spin up an ASP.NET Core application using the CreateHostBuilder
method found generally in our apps Program.cs
file.
We’ll also be using the IClassFixture
interface in XUnit to keep the instances of browsers to a minimum. Playwright is fast but requires some setup and startup time we’d like to avoid in every test. We’ll also specify Chromium as the browser we’ll be using for our tests. Let’s look at the core of our tests.
Note, you may need to set the content root of your web application if you have CSS and javascript assets. Use the webBuilder
to call UseContentRoot
with a path.
The WebServerFixture
class helps us create instances for our IPlaywright
and IBrowser
interfaces. Additionally, within the constructor, we set up our ASP.NET Core IHost
instance. We need to find a random open port to listen in on as well.
Before we start writing tests, let’s talk about the testing strategy. In my opinion, it’s best to decorate HTML elements with unique attributes that won’t change as our UI/UX experience evolves. I’ve added a pw-name
attribute to elements that describe a feature on the page.
Now, let’s write a test!
Wow! So straightforward. There is one downside to debugging tests, and that’s while we pause the debugger on a breakpoint, our server can’t process any requests, which makes exercising the app impossible without a few additional lines of code.
We’ll need to use the pause debugger feature, and the immediate window to change the value of pause
to continue our test.
Running the test, we see that the execution takes 524ms
, and that time includes starting Playwright, and our ASP.NET Core web application. Additional tests in our test suite would benefit from the shared instance of WebServerFixture
.
Conclusion
Playwright is a neat end-to-end library with benefits for ASP.NET Core developers. Playwright has additional APIs that allow us to take screenshots, exercise UI, and more. With the test fixture in this post, developers can write end-to-end tests that run inside CI/CD pipelines, giving everyone more confidence that the app’s behavior is intentional. I hope you found this post helpful, and as always, thanks for reading.