Tuesday, February 11, 2025
HomeJavaPlaywright meets JUnit 5 - Java Code Geeks

Playwright meets JUnit 5 – Java Code Geeks


Playwright is a Node.js-based device for automating browsers. It helps all trendy rendering engines together with Chromium, WebKit and Firefox. Playwright can be utilized with JavaScript, TypeScript, Python, .NET and Java. On this tutorial, we’ll discover the setup of a check automation venture utilizing Playwright for Java, JUnit 5 and Gradle. Additionally, you will be taught some fundamentals of Playwright instruments like codegen, Playwright Inspector and hint viewer. I can even present some fundamental setup for Docker in addition to GitHub Actions. Let’s get began!

Introduction

Disclaimer

Why Playwright?

I began utilizing Playwright (with Node.js) in June 2022, and I’m actually impressed with this device. Listed below are a number of the options I discover most necessary about Playwright:

  • Straightforward to make use of: Among the best issues about Playwright is that it’s packaged multi function and simple to arrange and use. Not like with Selenium, there’s no have to combine completely different instruments collectively.
  • Multi-language help: Playwright helps a number of programming languages, together with JavaScript, TypeScript, Python, .NET, and Java. Whereas the native Node.js model supplies the perfect expertise, for my part, due to its built-in check runner with highly effective configuration and reporting capabilities, Playwright makes it simpler for builders to put in writing assessments within the language they’re most snug with.
  • Multi-browser help: Playwright helps Chromium, Firefox, WebKit, and Opera. Configuration is straightforward, and there’s no want to put in extra drivers or seek the advice of third-party documentation. All these browsers’ drivers are maintained by the identical group.
  • Auto-wait performance: Playwright routinely waits for web page parts to be seen and interactive earlier than performing actions on them. This helps keep away from flakiness in assessments and makes them extra dependable.
  • Web page and browser context isolation: Playwright means that you can create a number of browser contexts and isolate assessments from one another simply. This supplies extra robustness and stability when operating assessments.
  • Tooling: Playwright comes with instruments that make it simple to handle and execute assessments from the command line, and combine with construct techniques and automation instruments. Codegen is one such device that means that you can file your interactions with an online utility and generate the code routinely.
  • Structure and limitations: Not like Cypress, which runs assessments in the identical runtime as the applying being examined, Playwright runs assessments in a separate course of. This supplies extra isolation and avoids potential points. Playwright has no issues with operating assessments in parallel, which may be a difficulty with Cypress except you pay.
  • Documentation: The Playwright documentation is well-organized, simple to navigate, and perceive. It incorporates a variety of examples and code snippets. I discovered it very useful and was capable of finding what I wanted with none issues. There are matters thought that aren’t that nicely defined or lack data (like for instance Docker part), however I’m certain will probably be improved sooner or later.

Playwright: Java vs Node.js

Though I don’t have a lot expertise with the Java model of Playwright but, I can say that because it goes to the API it is vitally just like the Node.js model. The principle distinction is that the Java model doesn’t have a built-in check runner, so you need to use a third-party one, equivalent to JUnit 5. And Node.js built-in check runner supplies a variety of helpful options equivalent to:

  • Visible testing out of the field through count on API.
  • Numerous configuration choices like timeout, retries, headless mode, browsers, viewports, reporters and way more.
  • Simply configurable tracing and video recording.
  • Constructed-in reporters together with the HTML reporter.

Why Playwright for Java?

Though the Node.js model at the moment seems to be superior to the Java model, I imagine it’s nonetheless worthwhile to think about using Playwright for Java. Listed below are some explanation why I feel it’s a related choice:

  • I feel that Playwright for Java is usually a trendy, dependable, and user-friendly different to Selenium for end-to-end testing in Java-based tasks – solely if you’re prepared for some compromises and further work round configuration.
  • I imagine that some groups might not be capable of use Node.js for numerous causes, so Playwright for Java could possibly be a superb different.
  • Playwright for Java can be utilized as a device for automating duties (like crawling net pages, scraping information, and many others.) and never essential to create end-to-end assessments. If that’s the case, the shortage of a built-in check runner is just not an issue.

Supply code

The whole code for this text may be discovered on GitHub: junit5-playwright-demo.

Conditions

What you’ll want to get began:

  • Terminal of your alternative
  • Git
  • Java 17 or greater
  • IntelliJ IDEA (or every other IDE of your alternative)

For Java I like to recommend asdf model supervisor. Yow will discover extra details about on my weblog: Handle a number of Java SDKs with asdf with ease .

Organising the venture with Gradle

To hurry up the method of organising the venture, I’ll use the junit5-gradle-template repository. It’s a template venture for JUnit 5 and Gradle particularly.

Be aware: There may be additionally an official starter by JUnit group that may be discovered right here: junit5-jupiter-starter-gradle

Steps:

  • Clone the template repository: git clone --depth=0 https://github.com/kolorobot/junit5-gradle-template my-playwright-project && cd my-playwright-project
  • Take away the .git listing: rm -rf .git
  • Execute ./gradlew clear check to confirm that all the pieces works as anticipated
  • Import venture to your IDE

Including Playwright dependency

To make use of Playwright with Java, we have to add the playwright dependency to construct.gradle file:

buildscript {
    ext {
        playwrightVersion = '1.30.0'
    }
}

implementation "com.microsoft.playwright:playwright:${playwrightVersion}"

The library is added as implementation dependency, so will probably be obtainable within the runtime classpath as nicely. This can enable us to make use of the library not solely in our assessments, but additionally in our utility code.

Playwright with no check runner

With dependency added, we will create a easy app that may open a browser and navigate to an internet site.

  • Create a brand new bundle in src/fundamental/java listing (e.g. pl.codeleak.demos.playwright)
  • Create a brand new class App in that bundle and add the next code:
bundle pl.codeleak.demos.playwright;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Web page;
import com.microsoft.playwright.Playwright;

public class App {
    public static void fundamental(String[] args) {
        strive (Playwright playwright = Playwright.create()) {
            Browser browser = playwright.chromium().launch();
            Web page web page = browser.newPage();

            web page.navigate("https://weblog.codeleak.pl/");
            System.out.println(web page.title());
        }
    }
}

The above code creates a Playwright occasion and launches a browser (on this case, Chromium) in headless mode and creates a brand new web page.

The web page navigates to an internet site, will get its title and prints it to the console. The code makes use of a try-with-resources assertion, which routinely closes the playwright object when the strive block is completed.

To run the browser in non-headless mode, we will modify the code as follows:

bundle pl.codeleak.demos.playwright;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Web page;
import com.microsoft.playwright.Playwright;

public class App {
    public static void fundamental(String[] args) {
        BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions()
                .setHeadless(false);

        strive (Playwright playwright = Playwright.create()) {
            Browser browser = playwright.chromium().launch(launchOptions);
            Web page web page = browser.newPage();

            web page.navigate("https://weblog.codeleak.pl/");
            System.out.println(web page.title());
        }
    }
}

To run the app, we will use run command, however first we have to modify construct.gradle file and configure the utility plugin:

plugins {
    id("utility")
}

utility {
    mainClass = "pl.codeleak.demos.playwright.App"
}

Now, we will run the app with ./gradlew run command.

❯ ./gradlew run

> Activity :run
weblog.codeleak.pl

BUILD SUCCESSFUL in 5s
2 actionable duties: 1 executed, 1 up-to-date

Working Playwright CLI instruments with Gradle

Playwright comes with a CLI instruments that may be helpful for code era, operating and debugging assessments or viewing the traces.

To run Playwright CLI with Gradle, we have to modify construct.gradle, add utility plugin, and create a customized playwright activity that executes com.microsoft.playwright.CLI:

apply plugin: 'utility'

duties.register('playwright', JavaExec) {
    classpath = sourceSets.fundamental.runtimeClasspath
    mainClass="com.microsoft.playwright.CLI"
}

Now, we will run Playwright CLI with Gradle:

./gradlew playwright --args="--help"

Generate code with codegen

As we arrange the Playwright CLI, we will use it to run codegen command. codegen is a device that may generate code snippets for you based mostly on the consumer interactions with an internet site utilizing Playwright Inspector. It means that you can file your interactions with an online web page after which generate code snippets in Java that can be utilized to automate these interactions.

As per the official documentation, Playwright Inspectoris a GUI device that helps writing and debugging Playwright scripts. That’s our default advisable device for scripts troubleshooting.

To be taught extra about codegen device and its choices, we will execute the next command:

./gradlew playwright --args="codegen --help"

As you observe the output, the codegen command supplies loads of choices. For instance, we will specify the browser to make use of or the machine to emulate, and many others. Let’s strive:

./gradlew playwright --args="codegen --browser chromium --device 'iPhone 13' https://weblog.codeleak.pl/"

As soon as the above command is executed, two home windows can be opened: Playwright Inspector and the browser that can be navigated to the desired URL. Now, we will work together with the web site and the codegen will generate code snippets for us. As soon as we’re completed with the interplay, we will copy the code and use it in our scripts.

JUnit 5 meets Playwright

Organising the bottom Playwright check

Within the earlier part, we realized easy methods to use Playwright as browser device in a easy utility. Now, we’ll discover ways to use it with JUnit 5.

Let’s create a base check class that can be utilized by all our assessments. It is going to arrange Playwright and create a browser occasion. The bottom check class can even create a brand new browser context and a brand new web page for every check methodology.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
summary class PlaywrightTest {

    Playwright playwright;
    Browser browser;

    BrowserContext context;
    Web page web page;

    @BeforeAll
    void launchBrowser() {
        playwright = Playwright.create();
        browser = playwright.chromium().launch();
    }

    @AfterAll
    void closeBrowser() {
        browser.shut();
        playwright.shut();
    }

    @BeforeEach
    void createBrowserContext() {
        context = browser.newContext();
        web page = context.newPage();
    }

    @AfterEach
    void closeBrowserContext() {
        web page.shut();
        context.shut();
    }
}

Let’s rapidly look at the above code:

  • The category is annotated with @TestInstance(TestInstance.Lifecycle.PER_CLASS), which implies that just one occasion of the check class is created for all check strategies, and the identical occasion is used for all check strategies within the class.
  • The category has 4 JUnit 5 lifecycle strategies:
    • @BeforeAll: the launchBrowser methodology launches a Chromium browser utilizing Playwright. It’s executed earlier than all check strategies within the class.
    • @AfterAll: the closeBrowser methodology closes the browser and Playwright gracefully. It’s executed in any case check strategies within the class.
    • @BeforeEach: the createBrowserContext methodology creates a brand new browser context and a brand new web page object for interacting with a browser. It’s executed earlier than every check methodology within the class.
    • @AfterEach: the closeBrowserContext methodology closes the web page and context after every check.

To set the default check occasion lifecycle to PER_CLASS for all assessments in JUnit 5, create a file referred to as junit-platform.properties in src/check/sources with the next Content material:

junit.jupiter.testinstance.lifecycle.default=per_class

JUnit 5 is the most recent model of the favored JUnit testing framework. It’s a full rewrite of the unique JUnit 4 framework. JUnit 5 is the primary model of JUnit to help Java 8 options equivalent to lambda expressions and default strategies. In case you are not conversant in JUnit 5, you may examine my JUnit 5 – Fast Tutorial publish.

Creating a primary check

Now, we will create our first check. Let’s create a check class that extends the PlaywrightTest class and create a easy check that navigates to this weblog and searches for “junit 5” time period. The check class will appear to be this:

bundle pl.codeleak.demos.playwright;
import org.junit.jupiter.api.Check;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

class BlogSearchTest extends PlaywrightTest {

    @Check
    void searchesForTermAndGetsResults() {
        web page.navigate("https://weblog.codeleak.pl");
        web page.locator("button[aria-label="Search"]").click on();
        web page.getByPlaceholder("Search this weblog").fill("junit 5");
        web page.getByPlaceholder("Search this weblog").press("Enter");

        assertThat(web page).hasURL("https://weblog.codeleak.pl/search?q=junit+5");
        assertThat(web page.locator("article .publish")).hasCount(20);
        assertThat(web page.getByText("Present posts matching the seek for junit 5"))
                .isVisible();
    }
}

Let’s rapidly look at the above code:

  • The check class extends the PlaywrightTest class, which implies that it inherits all of the setup and teardown strategies.
  • The check methodology makes use of the web page object to navigate to the weblog, clicks the search button, fills the search enter and press the Enter key.
  • It makes use of Playwright built-in assertions to examine that:
    • the URL incorporates the search time period
    • the search outcomes are displayed and there are 20 of them
    • the search outcomes header is seen

Now, we will run the check and observe the consequence:

./gradlew check --tests BlogSearchTest

> Activity :check

BlogSearchTest > searchesForTermAndGetsResults() FAILED
    org.opentest4j.AssertionFailedError at BlogSearchTest.java:19

1 check accomplished, 1 failed

> Activity :check FAILED

FAILURE: Construct failed with an exception.

* What went unsuitable:
Execution failed for activity ':check'.
> There have been failing assessments. See the report at: file:///construct/stories/assessments/check/index.html

Oops, the check failed (not less than it ought to). You may look at the report at construct/stories/assessments/check/index.html to see the small print. The rationale of failure may be rapidly seen within the report:

org.opentest4j.AssertionFailedError: Locator anticipated to be seen
Name log:
Locator.count on with timeout 5000ms
ready for getByText("Present posts matching the seek for junit 5")

As you may see, the check failed as a result of the anticipated search outcomes header is just not seen. To repair this we will look at the applying by manually executing the state of affairs, we will debug the check in IDE, or we will run the check in a debug mode with Playwright.

Let’s strive the final choice to be taught extra about Playwright Inspector.

Debugging the check with Playwright Inspector

You might be already conversant in Playwright Inspector from the earlier part. Now, we will use it to debug our check. To do this, we have to run the check in a debug mode:

PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=./src/check/java ./gradlew check --tests BlogSearchTest
  • PWDEBUG=1 allows the debug mode and begins the browser in headed mode (it’s possible you’ll recall that the bottom class launches the browser in headless mode).
  • PLAYWRIGHT_JAVA_SRC=./src/check/java tells Playwright to make use of the supply code from the src/check/java listing. It’s wanted to have the ability to see the supply code within the Playwright Inspector.

The inspector opens a browser window and highlights parts because the check is being executed. The toolbar supplies choices to play the check, step via every motion utilizing Step over, or resume the script. You’ve got entry to Actionability Logs that present some helpful information about actions being carried out. You may as well look at the web page by utilizing the Discover choice. Final however not least, you should utilize the browser developer instruments.

Recording a hint

You may as well file a hint of the check execution. To do this, you’ll want to use BrowserContext.tracing() API. Modify @BeforeEach and @AfterEach annotated strategies in PlaywrightTest as follows:

@BeforeEach
void createBrowserContext() {
    context = browser.newContext();
    context.tracing().begin(new Tracing.StartOptions()
            .setScreenshots(true)
            .setSnapshots(true));
    web page = context.newPage();
}

@AfterEach
void closeBrowserContext(TestInfo testInfo) {
    var traceName = testInfo.getTestClass().get().getSimpleName() +
            "-" + testInfo.getTestMethod().get().getName() + "-trace.zip";
    context.tracing().cease(new Tracing.StopOptions()
            .setPath(Paths.get("construct/stories/traces/" + traceName)));

    web page.shut();
    context.shut();
}

When you re-run the check, you will discover the hint within the construct/stories/traces listing. You may open it within the trace-view device like this:

./gradlew playwright --args="show-trace construct/stories/traces/BlogSearchTest-searchesForTermAndGetsResults-trace.zip"

Fixing the check

Since we positioned the rationale check was failing, we will repair it by modifying the final assertion (the textual content must be: “Exhibiting posts matching the seek for junit 5”:

assertThat(web page.getByText("Exhibiting posts matching the seek for junit 5"))
        .isVisible();

Web page Object sample with Playwright

As soon as the primary check is passing, we will transfer on to the subsequent one. This time can be creating assessments for TodoMVC Vanilla.js-based utility obtainable right here: http://todomvc.com/examples/vanillajs. The applying is a Single Web page Utility (SPA) and makes use of native storage as a activity repository. The doable situations to be applied embody including and modifying todo, eradicating todo, marking single or a number of todos as completed. The implementation can be completed utilizing Web page Object sampleakaPOP`.

The aim of POP is to summary the applying pages and performance from the precise assessments. POP improves re-usability of the code throughout assessments and fixtures but additionally makes the code simpler to keep up.

Let’s create an interface with the strategies that characterize situations that we are going to be automating:

bundle pl.codeleak.demos.playwright;

import java.util.Checklist;

interface TodoMvc {
    void navigateTo();
    void createTodo(String todoName);
    void createTodos(String... todoNames);
    int getTodosLeft();
    boolean todoExists(String todoName);
    int getTodoCount();
    Checklist<String> getTodos();
    void renameTodo(String todoName, String newTodoName);
    void removeTodo(String todoName);
    void completeTodo(String todoName);
    void completeAllTodos();
    void showActive();
    void showCompleted();
    void clearCompleted();
}

Implementing TodoMVC web page API

We’ll create a category TodoMvcPage that may implement the TodoMvc interface:

bundle pl.codeleak.demos.playwright;

import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Web page;

import java.util.Checklist;
import java.util.Objects;

public class TodoMvcPage implements TodoMvc {

    non-public Web page web page;

    public TodoMvcPage(Web page web page) {
        Objects.requireNonNull(web page, "Web page is required");
        this.web page = web page;
    }

    @Override
    public void navigateTo() {
        web page.navigate("https://todomvc.com/examples/vanillajs");
    }

    public void createTodo(String todoName) {
        web page.locator(".new-todo").kind(todoName);
        web page.locator(".new-todo").press("Enter");
    }

    public void createTodos(String... todoNames) {
        for (String todoName : todoNames) {
            createTodo(todoName);
        }
    }

    public int getTodosLeft() {
        return Integer.parseInt(web page.locator(".todo-count > sturdy").textContent());
    }

    public boolean todoExists(String todoName) {
        return getTodos().stream().anyMatch(todoName::equals);
    }

    public int getTodoCount() {
        return web page.locator(".todo-list li").depend();
    }

    public Checklist<String> getTodos() {
        return web page.locator(".todo-list li")
                .allTextContents();
    }

    public void renameTodo(String todoName, String newTodoName) {
        Locator todoToEdit = getTodoElementByName(todoName);
        todoToEdit.dblclick();
        Locator todoEditInput = todoToEdit.locator("enter.edit");
        todoEditInput.clear();
        todoToEdit.kind(newTodoName);
        todoToEdit.press("Enter");
    }

    public void removeTodo(String todoName) {
        Locator todoToRemove = getTodoElementByName(todoName);
        todoToRemove.hover();
        todoToRemove.locator("button.destroy").click on();
    }

    public void completeTodo(String todoName) {
        Locator todoToComplete = getTodoElementByName(todoName);
        todoToComplete.locator("enter.toggle").click on();
    }

    public void completeAllTodos() {
        web page.locator(".toggle-all").click on();
    }

    public void showActive() {
        web page.locator("a[href="#/active"]").click on();
    }

    public void showCompleted() {
        web page.locator("a[href="#/completed"]").click on();
    }

    public void clearCompleted() {
        web page.locator(".clear-completed").click on();
    }

    non-public Locator getTodoElementByName(String todoName) {
        return web page.locator(".todo-list li")
                .all()
                .stream()
                .filter(locator -> todoName.equals(locator.textContent()))
                .findFirst()
                .orElseThrow(() -> new RuntimeException("Todo with identify " + todoName + " not discovered!"));
    }
}

First, we navigate to the applying utilizing navigate methodology. Parts on that web page are positioned utilizing the locator methodology of the Playwrights Web page object. This methodology takes a selector (on this case CSS selector) as an argument and returns a Locator object that represents the set of parts matching that selector. The returned Locator object supplies strategies to work together with the positioned parts, equivalent to depend (returns the variety of parts), textContent (returns the textual content Content material of the primary ingredient), allTextContents (returns an inventory of textual content Contents of all parts), click on and dbclick (simulates a click on occasion on the primary ingredient), clear (clears the Content material of the primary ingredient), and kind (sorts textual content into the primary ingredient).

Learn extra about locators within the Playwright documentation right here and right here, and about actions right here.

Creating TodoMVC assessments

Earlier than creating precise assessments, let’s add AssertJ to our venture. We’ll use it to make assertions in our assessments.

Add the next dependency to construct.gradle:

buildscript {
    ext {
        assertJVersion = '3.21.0'
    }
}

dependencies {
    testImplementation "org.assertj:assertj-core:${assertJVersion}"
}

Now, we’re able to create the check class. Let’s see the code:

bundle pl.codeleak.demos.playwright;

import org.junit.jupiter.api.*;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

@DisplayName("Managing Todos")
class TodoMvcTests extends PlaywrightTest {

    TodoMvc todoMvc;

    non-public ultimate String buyTheMilk = "Purchase the milk";
    non-public ultimate String cleanupTheRoom = "Clear up the room";
    non-public ultimate String readTheBook = "Learn the e-book";

    @BeforeEach
    void navigateTo() {
        todoMvc = new TodoMvcPage(web page);
        todoMvc.navigateTo();
    }

    @Check
    @DisplayName("Creates Todo with given identify")
    void createsTodo() {
        // act
        todoMvc.createTodo(buyTheMilk);
        // assert
        assertAll(
                () -> assertThat(todoMvc.getTodosLeft()).isOne(),
                () -> assertThat(todoMvc.todoExists(buyTheMilk)).isTrue()
        );
    }

    @Check
    @DisplayName("Edits inline double-clicked Todo")
    void editsTodo() {
        // organize
        todoMvc.createTodos(buyTheMilk, cleanupTheRoom);
        // act
        todoMvc.renameTodo(buyTheMilk, readTheBook);
        // assert
        assertAll(
                () -> assertThat(todoMvc.todoExists(buyTheMilk)).isFalse(),
                () -> assertThat(todoMvc.todoExists(readTheBook)).isTrue(),
                () -> assertThat(todoMvc.todoExists(cleanupTheRoom)).isTrue()
        );
    }

    @Check
    @DisplayName("Removes chosen Todo")
    void removesTodo() {
        // organize
        todoMvc.createTodos(buyTheMilk, cleanupTheRoom, readTheBook);
        // act
        todoMvc.removeTodo(buyTheMilk);
        // assert
        assertAll(
                () -> assertThat(todoMvc.todoExists(buyTheMilk)).isFalse(),
                () -> assertThat(todoMvc.todoExists(cleanupTheRoom)).isTrue(),
                () -> assertThat(todoMvc.todoExists(readTheBook)).isTrue()
        );
    }

    @Check
    @DisplayName("Toggles chosen Todo as accomplished")
    void togglesTodoCompleted() {
        todoMvc.createTodos(buyTheMilk, cleanupTheRoom, readTheBook);

        todoMvc.completeTodo(buyTheMilk);
        assertThat(todoMvc.getTodosLeft()).isEqualTo(2);

        todoMvc.showCompleted();
        assertThat(todoMvc.getTodoCount()).isOne();

        todoMvc.showActive();
        assertThat(todoMvc.getTodoCount()).isEqualTo(2);
    }
    
    // The remainder of the assessments omitted for brevity.
}

Let’s look at the code a bit:

  • The navigateTo methodology can be referred to as earlier than every check, however after lifecycle strategies outlined within the base class. It is going to create a brand new occasion of TodoMvcPage class and navigate to the TodoMVC utility.
  • The @DisplayName annotation is used to supply a extra descriptive identify for the check. Will probably be displayed within the check report.
  • The assertAll methodology is used to group assertions. It is going to fail the check if any of the assertions fails. That is JUnit 5 characteristic.
  • The assertThat methodology is used to make assertions. It’s offered by AssertJ library. No Playwright built-in assertions are used as we wish to preserve the check code impartial of the underlying implementation.

Be aware: TodoMvc interface in addition to the check was adopted from the code I created for my Selenium and JUnit 5 tutorial. The tutorial may be discovered right here and the supply code right here.

Working the assessments

To run the assessments, execute the next command:

./gradlew check --tests TodoMvcTests

> Activity :check

Managing Todos > Creates Todo with given identify PASSED
Managing Todos > Creates Todos all with the identical identify PASSED
Managing Todos > Edits inline double-clicked Todo PASSED
Managing Todos > Removes chosen Todo PASSED
Managing Todos > Toggles chosen Todo as accomplished PASSED
Managing Todos > Toggles all Todos as accomplished PASSED
Managing Todos > Clears all accomplished Todos PASSED

BUILD SUCCESSFUL in 9s

Parameterized assessments

The final thought of parameterized unit assessments is to run the identical check methodology for various check information. To create a parameterized check in JUnit 5 you annotate a check methodology with @ParameterizedTest and supply the argument supply for the check methodology. There are a number of argument sources obtainable together with:

  • @ValueSource – offered entry to array of literal values i.e. shorts, ints, strings and many others.
  • @MethodSource – supplies entry to values returned from manufacturing unit strategies
  • @CsvSource – which reads comma-separated values (CSV) from a number of provided CSV traces
  • @CsvFileSource – which is used to load comma-separated worth (CSV) information

Create check information

In our instance, we’ll use CSV file as a supply of check information with the next Content material:

todo;completed
Purchase the milk;false
Clear up the room;true
Learn the e-book;false

Add this file to the src/check/sources listing.

Add dependency

To make use of parameterized assessments, we have to add the next dependency to the construct.gradle file:

testImplementation "org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}"

Create assessments

To create a parameterized check, we have to annotate the check methodology with @ParameterizedTest and supply the argument supply. The whole supply code of the TodoMvcParameterizedTests class is proven under:

bundle pl.codeleak.demos.playwright;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.supplier.CsvFileSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

class TodoMvcParameterizedTests extends PlaywrightTest {

    TodoMvcPage todoMvc;

    @BeforeEach
    void navigateTo() {
        todoMvc = new TodoMvcPage(web page);
        todoMvc.navigateTo();
    }

    @ParameterizedTest
    @CsvFileSource(sources = "/todos.csv", numLinesToSkip = 1, delimiter=";")
    @DisplayName("Creates Todo with given identify")
    void createsTodo(String todo) {
        todoMvc.createTodo(todo);
        assertSingleTodoShown(todo);
    }

    @ParameterizedTest(identify = "{index} - {0}, completed = {1}")
    @CsvFileSource(sources = "/todos.csv", numLinesToSkip = 1, delimiter=";")
    @DisplayName("Creates and optionally removes Todo with given identify")
    void createsAndRemovesTodo(String todo, boolean completed) {

        todoMvc.createTodo(todo);
        assertSingleTodoShown(todo);

        todoMvc.showActive();
        assertSingleTodoShown(todo);

        if (completed) {
            todoMvc.completeTodo(todo);
            assertNoTodoShown(todo);

            todoMvc.showCompleted();
            assertSingleTodoShown(todo);
        }

        todoMvc.removeTodo(todo);
        assertNoTodoShown(todo);
    }

    non-public void assertSingleTodoShown(String todo) {
        assertAll(
                () -> assertThat(todoMvc.getTodoCount()).isOne(),
                () -> assertThat(todoMvc.todoExists(todo)).isTrue()
        );
    }

    non-public void assertNoTodoShown(String todo) {
        assertAll(
                () -> assertThat(todoMvc.getTodoCount()).isZero(),
                () -> assertThat(todoMvc.todoExists(todo)).isFalse()
        );
    }
}

Run the parameterized assessments

To run the assessments, execute the next command:

./gradlew check --tests TodoMvcParameterizedTests

TodoMvcParameterizedTests > Creates and optionally removes Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsAndRemovesTodo(String, boolean)[1] PASSED

TodoMvcParameterizedTests > Creates and optionally removes Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsAndRemovesTodo(String, boolean)[2] PASSED

TodoMvcParameterizedTests > Creates and optionally removes Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsAndRemovesTodo(String, boolean)[3] PASSED

TodoMvcParameterizedTests > Creates Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsTodo(String)[1] PASSED

TodoMvcParameterizedTests > Creates Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsTodo(String)[2] PASSED

TodoMvcParameterizedTests > Creates Todo with given identify > pl.codeleak.demos.playwright.TodoMvcParameterizedTests.createsTodo(String)[3] PASSED

BUILD SUCCESSFUL in 8s

Learn additionally: Cleaner Parameterized Assessments with JUnit 5

Run all of the assessments

Thus far, we’ve fairly some assessments. Let’s run all them and see how lengthy it takes:

./gradlew clear check

BUILD SUCCESSFUL in 17s

Fairly lengthy. Can we pace it up?

Parallel assessments execution

JUnit 5 has built-in help for parallel assessments execution however by default assessments are executed sequentially. To alter this, we have to present a number of properties, however we additionally have to be sure that our Playwright based mostly assessments may be executed in parallel.

Playwright and thread-safety

Playwright for Java is just not thread-safe, which means that its strategies, in addition to the strategies of objects created by it (equivalent to BrowserContext, Browser, Web page, and many others.), ought to solely be accessed on the thread the place the Playwright object was created, or correct synchronization should be put in place to make sure that just one thread is accessing Playwright strategies at a time.

Provided that utilizing the identical Playwright objects throughout a number of threads with out correct synchronization is just not secure, it is strongly recommended to create a separate Playwright occasion for every thread and use it solely on that thread.

As it’s possible you’ll recall, our PlaywrightTest base class is annotated with @TestInstance(TestInstance.Lifecycle.PER_CLASS) to be sure that the Playwright occasion is created solely as soon as for the lifecycle of that class. This little trick does the job. No less than, a part of it. We nonetheless have to configure JUnit to run assessments in parallel.

JUnit 5 configuration for parallel execution

To allow parallel execution, we have to set a number of properties. We will present the properties on the command line or through junit-platform.prperties file.

For that, create junit-platform.properties file within the src/check/sources listing and add the next properties to it:

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.courses.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.issue=0.5

What it does, is that it allows parallel execution of separate check courses (check strategies in every class are executed sequentially) and dynamically makes use of as much as 50% of the obtainable CPU cores.

Let’s see if it really works:

./gradlew clear check

5 actionable duties: 5 executed
❯ ./gradlew clear check
<===========--> 87% EXECUTING [5s]
> :check > 0 assessments accomplished
> :check > Executing check pl.codeleak.demos.playwright.TodoMvcParameterizedTests
> :check > Executing check pl.codeleak.demos.playwright.BlogSearchTest
> :check > Executing check pl.codeleak.demos.playwright.TodoMvcTests


BUILD SUCCESSFUL in 11s

It really works! The assessments are executed in parallel, and it took solely 11 seconds to run all of them. You may need observed that each one 3 courses had been executed in parallel.

Learn extra about parallel execution of assessments in JUnit 5 Person Information

Primary GitHub Actions workflow

Organising a fundamental GitHub Actions workflow is fairly easy. One necessary half to recollect is that Playwright obtain all of the browsers on every run (~300 MB) so it could be a good suggestion to make use of cache. This will barely enhance the efficiency of the next workflow runs. Gradle construct motion helps caching its dependencies by default, however with Playwright we have to do it manually.

Let’s create a brand new workflow file referred to as run-tests.yml within the .github/workflows listing:

identify: Playwright Assessments

on: 
  workflow_dispatch:
  push:
    branches:
      fundamental
  
jobs:
  construct:
    runs-on: ubuntu-latest

    steps:
      - identify: Checkout
        makes use of: actions/checkout@v3

      - identify: Arrange JDK 17
        makes use of: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      - identify: Validate Gradle wrapper
        makes use of: gradle/wrapper-validation-action@v1

      - identify: Setup Gradle
        makes use of: gradle/gradle-build-action@v2
        
      - identify: Playwright cache
        makes use of: actions/cache@v3
        with:
          path: |
            ~/.cache/ms-playwright
          key: ms-playwright-${{ hashFiles('**/construct.gradle') }}

      - identify: Run assessments
        run: ./gradlew clear check

This workflow will run on each push to the fundamental department and on demand (workflow_dispatch). It is going to checkout the code, setup JDK 17, validate Gradlewrapper, setupGradleand cachePlaywright's ms-playwright` instantly. Lastly, it would run all of the assessments.

👉 No-cache run:

👉 With-cache run:

It’s possible you’ll additional analyze the ends in the Actions tab of your repository.

Please word, that is actually easy setup, and it doesn’t cowl all of the doable situations. For instance, it doesn’t run assessments on a number of browsers because the venture itself is just not ready for that.

Playwright for Java and Docker

Official Docker picture may be discovered right here. It incorporates all the required dependencies to run Playwright for Java assessments that are Java, Maven and the browsers. No Gradle is pre-installed, however since we’re utilizing Gradle Wrapper, we don’t really need it.

Working assessments in Docker

Let’s get began with pulling the picture and operating assessments manually. As a way to that, run the next instructions contained in the venture listing:

docker pull mcr.microsoft.com/playwright/java:v1.30.0-focal
docker run -it --rm --ipc=host -v $PWD:/assessments mcr.microsoft.com/playwright/java:v1.30.0-focal /bin/bash

The above instructions will pull the picture and run a container with the picture. The container can be eliminated after it exits. The --ipc=host flag is used to share the host’s IPC namespace with the container, and it is strongly recommended for Playwright to work correctly. The -v $PWD:/assessments flag is used to mount the present listing as a quantity contained in the container. The amount is mounted to the /assessments listing contained in the container.

As soon as the container is operating, we will run the assessments:

root@0644b3386f94:/# cd assessments/
root@0644b3386f94:/assessments# ./gradlew clear check
Downloading https://providers.gradle.org/distributions/gradle-8.0-bin.zip

Welcome to Gradle 8.0!

Beginning a Gradle Daemon (subsequent builds can be quicker)
Invalid Java set up discovered at '/usr/lib/jvm/openjdk-17' (Widespread Linux Areas). Will probably be re-checked within the subsequent construct. This may need efficiency impression if it retains failing. Run the 'javaToolchains' activity for extra particulars.

> Activity :check

<===========--> 87% EXECUTING [13s]
> :check > 11 assessments accomplished
> :check > Executing check pl.codeleak.demos.playwright.TodoMvcParameterizedTests
> :check > Executing check pl.codeleak.demos.playwright.TodoMvcTests


BUILD SUCCESSFUL in 51s
5 actionable duties: 5 executed

Be aware: Because it goes to Invalid Java set up discovered error, there appear to be answer within the subsequent model of Gradle: https://github.com/gradle/gradle/pull/23643

Making a Dockerfile

So, we confirmed the picture can be utilized to run the assessments, however we nonetheless have to create a Dockerfile to construct our personal picture. Let’s create a Dockerfile within the venture listing:

# Prolong official Playwright for Java picture
FROM mcr.microsoft.com/playwright/java:v1.30.0-focal

# Set the work listing for the applying
WORKDIR /assessments

# Copy the wanted information to the app folder in Docker picture
COPY gradle /assessments/gradle
COPY src /assessments/src
COPY construct.gradle /assessments
COPY gradle.properties /assessments
COPY gradlew /assessments
COPY settings.gradle /assessments

# Set up dependencies to hurry up subsequent check runs
RUN ./gradlew --version

This Dockerfile extends the official Playwright for Java picture and units the working listing for the assessments. It copies the required information to the app and installs the dependencies to hurry up subsequent check runs.

Now, we will construct the picture:

docker construct -t playwright-java-tests .

And run assessments within the container:

docker run -it --rm --ipc=host playwright-java-tests ./gradlew clear check

Conclusion

I imagine that Playwright for Java is a contemporary and developer-friendly different to Selenium and Selenium-based instruments and frameworks for end-to-end testing in Java-based tasks. Nevertheless, utilizing Playwright for Java might require some compromises and further work for correct configuration. It’s necessary to notice that the Node.js model of Playwright is highly effective sufficient to deal with complicated end-to-end testing.

Though I at the moment think about the Node.js model of Playwright to be superior, I nonetheless imagine that the Playwright for Java choice is price contemplating for just a few causes. Firstly, for groups which can be unable to make use of Node.js, Playwright for Java supplies a viable different for end-to-end testing. Secondly, it will also be used for automating duties equivalent to crawling net pages and scraping information and even Robotic Course of Automation (RPA) and never only for creating end-to-end assessments.

When you have any strategies or enhancements, please be at liberty to contact me through Twitter, LinkedIn, or report a difficulty within the venture repository.

References

Revealed on Java Code Geeks with permission by Rafal Borowiec, accomplice at our JCG program. See the unique article right here: Playwright meets JUnit 5

Opinions expressed by Java Code Geeks contributors are their very own.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments