Tutorial Unit and E2E Testing in Blazor - Part 2 Playwright Introduction

Why having end to end tests

As described in Part 1 we need end to end tests for various reasons:

  • We want to check if every real (in the sense of not a mocked one) component and service is working together
    • That means our database, application layer, UI layer in one. Maybe even external services (which could make the tests less stable as you are now dependent on the availability of this service)

When to use it

That part seems very debatable. Depending on the source you read you will find anything from almost nothing to just test everything. So the next part is just personal preference, please take it with a grain of salt.

Let's have a look at the testing pyramid:

testing pyramid

As we can see Unit tests are building your foundation. There are cheap to write and execute. Therefore they give you valuable and fast feedback. They should cover the base of your application.

Integration tests, as the name suggest, integrate multiple components in one system but they don't cross system boundaries. For example you have one component fetching data from your database in a form of a stored procedure and passing those afterwards to the UI. This behavior can't be mocked away. You want to test the stored procedure and that the data is passed correctly in your system.

E2E tests can cross system boundaries. Let's assume you have a login page which is using an identity server. The server is not directly part of your system, but it is a mandatory step. Therefore you can check the login procedure and they you land on the correct landing page in an E2E test.

My recommendation is the following:

  • Try to test as much as possible with unit and integration tests
  • Use E2E tests sparely.
  • Do not test exceptional cases. They can most likely be tested with unit or integration tests
  • Smoke tests are a good approach for E2E tests

Playwright

Here the official introduction from the github page of playwright: Playwright for .NET is the official language port of Playwright, the library to automate Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast.

Let's go!

We will take our example from Part 1 and try to e2e test the counter behavior.

Install the tools for Playwright

The first step is to install all tools so that we can run our test. Either head over to Playwright and follow the steps or do the following:

  • Run dotnet tool install --global Microsoft.Playwright.CLI from a shell
  • Run playwright install (depending on your shell you have to restart your shell as the command is not recognized)

Create a new Unit Test Project

We create a new unit test project and add the Playwright package:

PM > Install-Package PlaywrightSharp -Version 0.192.0

This time we do not need the reference to our productive assembly. This because we are solely relying on the structure of the document.

Now to the test:

using System.Threading.Tasks;
using Microsoft.Playwright;
using Xunit;

namespace BlazorTests.E2ETests
{
    public class IndexTitleTests
    {
        [Fact]
        public async Task ShouldIncreaseCounterByOne()
        {
            // Arrange
            using var playwright = await Playwright.CreateAsync();
            var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
            {
                Headless = true,
            });
            var page = await browser.NewPageAsync();
            await page.GotoAsync("https://localhost:5001/counter");
            await page.ScreenshotAsync(new PageScreenshotOptions {Path = "before_click.png"});
            
            // Act
            await page.ClickAsync(":text('Click me')", new PageClickOptions
            {
                ClickCount = 2,
            });

            // Assert
            await page.ScreenshotAsync(new PageScreenshotOptions {Path = "after_click.png"});
            var counter = await page.QuerySelectorAsync("p");
            var content = await counter.InnerTextAsync();
            Assert.Equal("Current count: 2", content);
        }
    }
}

Before we go into detail what really happens, just run this test and look what happens. Most likely you will receive the following error:

BlazorTests.E2ETests.IndexTitleTests.ShouldIncreaseCounterByOne

Microsoft.Playwright.PlaywrightException: net::ERR_CONNECTION_REFUSED at https://localhost:5001/counter

In contrast to our Unit Tests were we had a reference to the component itself, here we can not rely on that. Therefore we need a real running server with our website. If you just run your project you should be good to go and the test should turn green. If you are running with IIS Express you have to adapt the port in page.GotoAsync(...).

Now let's decompose the test and look what it does.

using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
    Headless = true,
});

The main goal is to create a real browser. In my case I used Chromium but you can also use Firefox or Webkit. This enables you to test your website with different browser. Also there is an Headless option which is set to true (this is the default). If Headless is false Playwright will open a real browser window and click through your application. That is very helpful in case of red tests or exceptions. You also have the SlowMo property. This gives the timespan in milliseconds between each action. I would propose if you try the headful approach and want to check what playwright is doing set it something like 2000 (2 seconds).

var page = await browser.NewPageAsync();
await page.GotoAsync("https://localhost:5001/counter");
await page.ScreenshotAsync(new PageScreenshotOptions {Path = "before_click.png"});

We now create a new page and navigate to it. We do not have to take care of when the site is finished loading. That is why we use playwright. It does it for us. The ScreenshotAsync is a nice function if you want to see what the browser would see in that moment. You can also use it to keep your documentation pictures up to date.

The act part:

await page.ClickAsync(":text('Click me')", new PageClickOptions
{
    ClickCount = 2,
});

Playwright works in the same way bUnit does. We use Css-Selectors to get to our elements and interact with them. In my example I choose to look for an HTML-element with has "Click me" as text. Once found playwright should click twice on it.

await page.ScreenshotAsync(new PageScreenshotOptions {Path = "after_click.png"});
var counter = await page.QuerySelectorAsync("p");
var content = await counter.InnerTextAsync();
Assert.Equal("Current count: 2", content);

And again we use query selector to get the p element (it is the only in the page) and check how the content is after the two clicks. Also here we do not have to implement anything to know when the click is done, playwright does it for us. I also took another screenshot after the click. Both screenshots look like this:

before after
before after

That's it. That was our first small test. Pretty straightforward.

Structure of the E2E test

It is common to have folder for pages. Each page in this folder really represent one page which is functionality in your app. Furthermore your page consists out of multiple reusable component like buttons, input fields, ... These we will also put into another folder. At the end our test code itself will consist only out of a few lines of code. But those few lines are super readable and maintainable. More to that in a later part of the series.

Conclusion and what comes next

We saw how to setup a basic project for Playwright and have our first test to click a button twice. For more information look into the document of plawright. In the next part we will look how we can structure our tests better.

Resources:

Git-Repository of the series: BlazorTests

UNO Platform - Build a Todo App - Part 2

This is the second part of our small mini series, where we build a todo app Kanban style with the UNO Platform. In this episode we are laying some ground work as well as defining all of our requirements we want to achieve.

We will slowly start creating our first components and make everything work smoothly together.

From Testing Pyramid to Diamond

In this article, we will discuss the testing pyramid - what it is and what are some problems with that.

We will also discuss a different approach: The testing diamond.

Tutorial Unit and E2E Testing in Blazor - Part 1

This blog post should give you an easy and good introduction how to unit and end-to-end test your Blazor Application. Furthermore it does not matter if we are running server side or client side aka WebAssembly. The main two libraries we are using is first bUnit for unit-testing and Playwright for end-to-end testing. So let's dive in!

An error has occurred. This application may no longer respond until reloaded. Reload x