Beginner Chapter 3 · 12 min read

Writing Your First Test

Walk through a fully annotated test that navigates to a page, interacts with elements, and verifies the outcome.

Anatomy of a Playwright Test

Every Playwright test follows the same three-phase structure: Arrange (set up the browser state), Act (perform the user actions), and Assert (verify the expected outcome). Playwright's test runner provides a page fixture automatically, so you never need to instantiate a browser manually inside a test.

Tests are grouped into files using test.describe blocks (JavaScript) or standard class groupings (Java/Python). Within each group you can define beforeEach and afterEach hooks to avoid repetition — for example, navigating to the login page before every test in a suite.

Tip: Use test.only (JS) or pytest -k (Python) while developing to run a single test in isolation without executing the entire suite.

Navigating, Clicking, and Asserting

The example below tests a simple login form. It navigates to the login page, fills in credentials, clicks the submit button, and then asserts that the dashboard heading is visible. Notice how there is no sleep() — Playwright automatically waits for each element to be ready before interacting with it, and for the navigation to complete before asserting.

Running and Inspecting Results

Run your test with npx playwright test or pytest. On failure Playwright captures a screenshot and, if configured, a video and a trace file that you can open in the Playwright Trace Viewer to see every step alongside DOM snapshots, network requests, and console output.

login.spec.js
import { test, expect } from '@playwright/test';

// Group related tests with describe
test.describe('Login flow', () => {

  // Navigate before every test in this group
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('successful login redirects to dashboard', async ({ page }) => {
    // Arrange — fill in credentials
    await page.getByLabel('Email').fill('user@example.com');
    await page.getByLabel('Password').fill('secret123');

    // Act — submit the form
    await page.getByRole('button', { name: 'Sign in' }).click();

    // Assert — dashboard heading should be visible
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
    await expect(page).toHaveURL('/dashboard');
  });

  test('wrong password shows error message', async ({ page }) => {
    await page.getByLabel('Email').fill('user@example.com');
    await page.getByLabel('Password').fill('wrongpass');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await expect(page.getByText('Invalid credentials')).toBeVisible();
  });
});
LoginTest.java
import com.microsoft.playwright.*;
import com.microsoft.playwright.assertions.PlaywrightAssertions;
import org.junit.jupiter.api.*;

class LoginTest {
  static Playwright playwright;
  static Browser browser;
  Page page;

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

  @BeforeEach
  void createPage() {
    page = browser.newPage();
    page.navigate("http://localhost:3000/login");
  }

  @Test
  void successfulLogin() {
    page.getByLabel("Email").fill("user@example.com");
    page.getByLabel("Password").fill("secret123");
    page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign in")).click();
    PlaywrightAssertions.assertThat(page).hasURL("/dashboard");
  }

  @AfterEach
  void closePage() { page.close(); }

  @AfterAll
  static void closeBrowser() { browser.close(); playwright.close(); }
}
test_login.py
import pytest
from playwright.sync_api import Page, expect

@pytest.fixture(autouse=True)
def go_to_login(page: Page):
    # Navigate before every test in this module
    page.goto("/login")

def test_successful_login(page: Page):
    # Arrange
    page.get_by_label("Email").fill("user@example.com")
    page.get_by_label("Password").fill("secret123")

    # Act
    page.get_by_role("button", name="Sign in").click()

    # Assert
    expect(page).to_have_url("/dashboard")
    expect(page.get_by_role("heading", name="Dashboard")).to_be_visible()

def test_wrong_password_shows_error(page: Page):
    page.get_by_label("Email").fill("user@example.com")
    page.get_by_label("Password").fill("wrongpass")
    page.get_by_role("button", name="Sign in").click()
    expect(page.get_by_text("Invalid credentials")).to_be_visible()

Playwright Beginner Writing Your First Test

Written by PV

© 2026 All Rights Reserved