Test Structure and Steps

Back

Loading concept...

🎭 Playwright Test Organization: Building Your Testing Kingdom

Imagine you’re building a LEGO castle. You don’t just dump all pieces on the floor and hope for the best. You organize them into groups, follow steps in order, and make sure everything is ready before you start. That’s exactly what Playwright test organization does for your code!


🏰 The Castle Analogy

Think of your test suite as a magical castle:

  • Test files = Different rooms in the castle
  • test.describe = Grouping rooms by purpose (bedrooms, kitchens)
  • Hooks = Castle servants who prepare and clean rooms
  • test.step = Step-by-step instructions in a recipe book

Let’s explore each room!


📁 Test File Structure

Your test files are like different buildings in your castle town.

Where Do Tests Live?

my-project/
├── tests/
│   ├── login.spec.ts
│   ├── cart.spec.ts
│   └── checkout.spec.ts
└── playwright.config.ts

Simple Rules:

  • Put tests in a tests folder
  • Name files with .spec.ts or .test.ts
  • One file = One feature (login, cart, etc.)

Think of it like this: You wouldn’t put your toys AND your clothes in the same drawer. Tests for login go in login file!


✨ The test Function: Your Basic Building Block

Every test starts with the test function. It’s like saying: “Here’s one thing I want to check!”

import { test, expect } from '@playwright/test';

test('page has title', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle('Example');
});

Breaking It Down:

Part What It Does
test Tells Playwright “this is a test”
'page has title' Name of your test (be descriptive!)
async ({ page }) Gets a fresh browser page
Inside { } Your test steps

Like a recipe: “Test that cake is sweet” → Open box → Taste cake → Check if sweet!


📦 test.describe: Grouping Related Tests

test.describe is like putting related toys in the same toy box.

import { test, expect } from '@playwright/test';

test.describe('Login Page', () => {

  test('shows login form', async ({ page }) => {
    await page.goto('/login');
    await expect(page.locator('form')).toBeVisible();
  });

  test('shows error for wrong password', async ({ page }) => {
    await page.goto('/login');
    await page.fill('#password', 'wrong');
    await page.click('button[type="submit"]');
    await expect(page.locator('.error')).toBeVisible();
  });

});

Why Group Tests?

  • Easy to find: All login tests together
  • Shared setup: Hooks work for the whole group
  • Better reports: See results by feature
graph TD A["test.describe: Login"] --> B["test: shows form"] A --> C["test: wrong password error"] A --> D["test: successful login"]

🎬 test.beforeAll: The Opening Ceremony

beforeAll runs ONCE before ALL tests in a group start. Like a chef preparing ingredients before cooking multiple dishes.

import { test, expect } from '@playwright/test';

test.describe('Shopping Cart', () => {

  test.beforeAll(async () => {
    console.log('Setting up test database...');
    // This runs ONCE before any test
  });

  test('can add item', async ({ page }) => {
    // This test runs after beforeAll
  });

  test('can remove item', async ({ page }) => {
    // beforeAll does NOT run again
  });

});

Perfect For:

  • Starting a test database
  • Creating test users
  • Loading shared test data

Real World: Before a school day, the janitor unlocks ALL doors once. Not before each class!


🎭 test.afterAll: The Closing Ceremony

afterAll runs ONCE after ALL tests finish. Like cleaning up after a party.

import { test } from '@playwright/test';

test.describe('User Tests', () => {

  test.afterAll(async () => {
    console.log('Cleaning up test data...');
    // Delete test users, close connections
  });

  test('create user', async ({ page }) => {
    // test runs
  });

  test('delete user', async ({ page }) => {
    // test runs, THEN afterAll runs
  });

});

Perfect For:

  • Deleting test data
  • Closing database connections
  • Generating reports

🔄 test.beforeEach: Fresh Start Every Time

beforeEach runs before EVERY single test. Like making your bed every morning.

import { test, expect } from '@playwright/test';

test.describe('Dashboard', () => {

  test.beforeEach(async ({ page }) => {
    // This runs before EACH test
    await page.goto('/dashboard');
    await page.fill('#username', 'testuser');
    await page.fill('#password', 'secret');
    await page.click('#login');
  });

  test('shows welcome message', async ({ page }) => {
    // Already logged in!
    await expect(page.locator('.welcome')).toBeVisible();
  });

  test('shows user stats', async ({ page }) => {
    // Also already logged in!
    await expect(page.locator('.stats')).toBeVisible();
  });

});

The Magic:

graph TD A["beforeEach: Login"] --> B["Test 1: welcome"] C["beforeEach: Login"] --> D["Test 2: stats"] E["beforeEach: Login"] --> F["Test 3: settings"]

No repeated code! Login once in beforeEach, use everywhere.


🧹 test.afterEach: Clean Slate After Every Test

afterEach runs after EVERY single test. Like washing your plate after each meal.

import { test } from '@playwright/test';

test.describe('File Upload', () => {

  test.afterEach(async ({ page }) => {
    // Runs after EACH test
    console.log('Clearing uploaded files...');
    // Clean up any files created during test
  });

  test('upload image', async ({ page }) => {
    // Upload an image
    // afterEach cleans it up!
  });

  test('upload document', async ({ page }) => {
    // Upload a document
    // afterEach cleans it up too!
  });

});

Perfect For:

  • Clearing cookies/storage
  • Deleting files created during test
  • Taking screenshots on failure

👣 test.step: Breaking Tests into Clear Steps

test.step makes your tests tell a story. Each step is like a chapter in a book.

import { test, expect } from '@playwright/test';

test('complete checkout', async ({ page }) => {

  await test.step('Go to shop', async () => {
    await page.goto('/shop');
  });

  await test.step('Add item to cart', async () => {
    await page.click('.product');
    await page.click('.add-to-cart');
  });

  await test.step('Fill shipping info', async () => {
    await page.fill('#address', '123 Main St');
    await page.fill('#city', 'New York');
  });

  await test.step('Complete payment', async () => {
    await page.fill('#card', '4111111111111111');
    await page.click('#pay');
    await expect(page.locator('.success')).toBeVisible();
  });

});

Why Use Steps?

Benefit Example
Clear Reports See exactly where test failed
Documentation Test reads like instructions
Debugging Find problems faster

Think of it: Instead of “make dinner”, you have:

  1. Chop vegetables
  2. Boil water
  3. Add pasta
  4. Serve!

🔁 The Complete Lifecycle

Here’s how ALL hooks work together:

graph TD A["beforeAll"] --> B["beforeEach"] B --> C["Test 1"] C --> D["afterEach"] D --> E["beforeEach"] E --> F["Test 2"] F --> G["afterEach"] G --> H["afterAll"]

Real Example:

import { test, expect } from '@playwright/test';

test.describe('Blog Posts', () => {

  test.beforeAll(async () => {
    console.log('🚀 Starting: Create database');
  });

  test.beforeEach(async ({ page }) => {
    console.log('📝 Before test: Login');
    await page.goto('/login');
  });

  test('create post', async ({ page }) => {
    await test.step('Click new post', async () => {
      await page.click('.new-post');
    });
    await test.step('Write content', async () => {
      await page.fill('.editor', 'Hello World!');
    });
  });

  test.afterEach(async () => {
    console.log('🧹 After test: Clear session');
  });

  test.afterAll(async () => {
    console.log('🏁 Finished: Close database');
  });

});

🎯 Quick Reference

Hook Runs Use For
beforeAll Once before all tests Setup database, create users
afterAll Once after all tests Cleanup, close connections
beforeEach Before each test Login, navigate to page
afterEach After each test Clear data, take screenshots
test.step Inside test Organize test into chapters

🌟 You Did It!

You now understand how to organize Playwright tests like a pro! Remember:

  1. Files = Separate features
  2. describe = Group related tests
  3. Hooks = Setup and cleanup helpers
  4. Steps = Tell a clear story

Your tests will be clean, organized, and easy to understand. Just like a well-built LEGO castle! 🏰


Next time you write a test, ask yourself: “Where does this belong? What needs to happen before? What needs to happen after?” Your future self will thank you!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.