DEV Community

Cover image for #002 | Getting Started - Writing Tests!
Nitya Narasimhan for Microsoft Azure

Posted on • Originally published at nitya.github.io on

#002 | Getting Started - Writing Tests!

This post was originally published on the Learn Playwright blog. Reference the original post to get the most updated version of this content.


🔖 | Today's Resources


🎯 Day 2 Objectives: #30DaysOfPlaywright!!

Our Day 1 exploration of Playwright helped us setup and validate our local testing envrionment. Today we'll complete the tutorial by reviewing fundamental Playwright concepts namely:

We'll quickly go over what each does and why we need them - and revisit each topic for a deeper dive in a future post. Let's get started!


1. Playwright Fundamentals

In my kickoff post I recommended the Introduction to Playwright Test runner talk as a good introduction to Playwright fundamentals. Today we'll reference it in the context of various sections of the Getting Started guide.

Here's a handy list of segments with direct links to the relevant section of the talk, for convenience. I recommend you check out at least Ch 1-3 for today.


2. Unpack the Test Script

Let's revisit the code fr the first test we created and ran on Day 1.

const { test, expect } = require('@playwright/test');

test('basic test', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  const title = page.locator('.navbar__inner .navbar__title');
  await expect(title).toHaveText('Playwright');
});
Enter fullscreen mode Exit fullscreen mode

This simple test script illustrates some fundamental Playwright concepts that enable more reliable end-to-end testing:

  • Auto-waiting - performs actionability checks on elements before making a requested action.
  • Web-First Assertions - retries checks until a necessary condition is met, before proceeding.
  • Test Isolation with fixtures - establish reliable environment for tests, enable parallelization.
  • Test Hooks - for just-in-time setup and teardown of resources shared between tests.

Let's explore these briefly below.


3. Web Assertions.

Playwright uses the expect library for test assertions. Developers can test if the current state of the application matches an expected result - with helpful matchers to test various conditions for that state. For example:

expect(value).toEqual(0);
Enter fullscreen mode Exit fullscreen mode

Modern web applications are dynamic with application state in constant flux. Testing assertions can be challenging if the matcher condition is not stable. Playwright makes this more reliable by extending the expect library with convenience async matchers that will now wait till the expected condition is met (and timeout otherwise).

await expect(page).toHaveTitle("Playwright");
Enter fullscreen mode Exit fullscreen mode

Now let's try the Writing Assertions example (see below):

// assert.spec.js
const { test, expect } = require('@playwright/test');

test('my test', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);

  // Expect an attribute "to be strictly equal" to the value.
  await expect(page.locator('text=Get Started').first()).toHaveAttribute('href', '/docs/intro');

  // Expect an element "to be visible".
  await expect(page.locator('text=Learn more').first()).toBeVisible();

  await page.click('text=Get Started');
  // Expect some text to be visible on the page.
  await expect(page.locator('text=Introduction').first()).toBeVisible();
});
Enter fullscreen mode Exit fullscreen mode

Save it to a new file (assert.spec.js) and run it as follows:

$ npx playwright test assert.spec.js
Enter fullscreen mode Exit fullscreen mode

The { page } argument provided to the test function is an example of a test fixture. We'll talk about what fixtures are in the next section.

For now, we can look at the page class documentation and see that it provides methods to interact with a single tab in a browser and handle various events emitted from it.

  • The page.locator() method resolves to a view of elements in that page that match the associated selector.
  • The page.goto() method navigates the browser to the provided URL.
  • The page.click() methods 'clicks' the element matching that selector.

We'll explore available assertions in Playwright in depth in a future post.


4. Test Fixtures.

The { page } argument passed into the test() function above is an example of a test fixture. So, what is a fixture?

Inspired by pytest, test fixtures are a tool for establishing a reliable and consistent test environment that provides tests exactly what they need for the current run - and nothing more. Playwright Test analyzes all the fixtures a test needs, merges required values into a single object - and makes that available to the test as a first parameter. The fixture is torn down after the test completes.

Why fixtures? Because they create a consistent environment (test repeatability) and provide effective isolation for testing with the following benefits:

  • Efficiency - run multiple tests in parallel, faster, with less memory used.
  • Targeting - retry just failed tests without re-running the entire suite.
  • Grouping - group tests based on shared meaning, not just setup.

Playwright comes with a number of built-in fixtures that you can configure (see below). You can also add your own:

You can use Test Fixtures at the granularity of a test run, or use Worker Fixtures to apply them at the level of a worker process (across all tests it oversees).

The bottom line is fixtures enable consistent and isolated testing environments in Playwright Test in a flexible and customizable manner.


5. Test Hooks.

While fixtures allow you to setup the environment for a test run, test hooks provide placeholders for writing code needed to setup and teardown resources that may be shared across tests (in a group) or used on a per-test basis. Here's an example from the getting started guide:

// example.spec.js
const { test, expect } = require('@playwright/test');

test.describe('feature foo', () => {
  test.beforeEach(async ({ page }) => {
    // Go to the starting url before each test.
    await page.goto('https://playwright.dev/');
  });

  test('my test', async ({ page }) => {
    // Assertions use the expect API.
    await expect(page).toHaveURL('https://playwright.dev/');
  });
});
Enter fullscreen mode Exit fullscreen mode

Here, the test.describe method is used to declare a group of tests. The test hooks we need to familiarize ourselves with are:


Day 2: Review & Resources

This has been a lot to absorb - but never fear. In the upcoming days, we'll dive into these topics and APIs in more detail with simple examples. For now, you have a working Playwright environment and a grasp of key terms and concept for end-to-end testing using this framework! Onwards!


Day 3: Up Next

We now have a sense of how to create and run a simple test, and the core components that make up a test script. In the next few posts, let's explore some of the tools available for authoring and debugging Playwright test scripts - before we start diving into the Playwright API in more detail.

Here is a short list of tooling resources that may be worth scanning for a head start.

In addition, check out the Community Showcase for tools, frameworks and examples authored by the broader community.


Discussion (0)