Test Control Methods

Back

Loading concept...

Playwright Test Control Methods 🎮

The Traffic Light Analogy 🚦

Imagine you’re a traffic controller at a busy intersection. You have special buttons that tell cars when to go, when to stop, when to slow down, and when to take a detour. Playwright’s test control methods work exactly like those buttons—they tell your tests what to do!


What Are Test Control Methods?

When you write tests, sometimes you need to:

  • Skip a test (like closing a road for repairs)
  • Run only one test (like letting just one car through)
  • Mark a test as broken (like putting up a “Road Closed” sign)
  • Mark a test to fix later (like scheduling road repairs)
  • Slow down a test (like a school zone speed limit)
  • Tag tests (like labeling roads by type)
  • Run tests conditionally (like opening roads only on certain days)

Let’s explore each one!


1. test.skip() - The “Closed Road” Sign 🚧

What Is It?

test.skip() tells Playwright: “Don’t run this test right now.”

It’s like putting a “Road Closed” sign—cars (tests) see the sign and take another route.

When Do We Use It?

  • A feature isn’t ready yet
  • A test works on some browsers but not others
  • You’re waiting for a bug fix

Simple Example

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

// This test will NOT run
test.skip('login with magic link', async ({ page }) => {
  // This code won't execute
  await page.goto('/magic-login');
});

Skip With a Reason

test.skip('payment checkout', async ({ page }) => {
  test.skip(true, 'Payment API not ready');
  await page.goto('/checkout');
});

Skip Based on Condition

test('mobile menu', async ({ page, isMobile }) => {
  test.skip(!isMobile, 'Only runs on mobile');
  await page.click('.hamburger-menu');
});

2. test.only() - The “VIP Lane” 🌟

What Is It?

test.only() tells Playwright: “Run ONLY this test, ignore all others.”

It’s like a VIP lane—only special cars get through while everyone else waits.

When Do We Use It?

  • Debugging a specific test
  • Working on one feature at a time
  • Quick testing during development

Simple Example

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

test('normal test 1', async ({ page }) => {
  // This will NOT run
});

// Only this test runs!
test.only('my special test', async ({ page }) => {
  await page.goto('/special-page');
});

test('normal test 2', async ({ page }) => {
  // This will NOT run either
});

Multiple Only Tests

test.only('first VIP test', async ({ page }) => {
  // This runs
});

test.only('second VIP test', async ({ page }) => {
  // This also runs
});

test('regular test', async ({ page }) => {
  // This does NOT run
});

Warning! Don’t commit test.only() to your codebase. It’s for local development only!


3. test.fail() - The “Expected Pothole” Sign ⚠️

What Is It?

test.fail() tells Playwright: “This test SHOULD fail. If it passes, something’s wrong!”

It’s like marking a pothole on the road—you EXPECT cars to bump there. If they don’t bump, the pothole must be fixed (which is great news!).

When Do We Use It?

  • You know there’s a bug
  • You want the test to track when a bug gets fixed
  • Documenting known issues

Simple Example

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

test.fail('broken logout button', async ({ page }) => {
  // This test is EXPECTED to fail
  await page.goto('/app');
  await page.click('#logout');
  // Bug: logout doesn't work yet
});

Fail With Condition

test('download feature', async ({ page, browserName }) => {
  test.fail(browserName === 'firefox',
    'Download broken in Firefox');
  await page.click('#download-btn');
});

What Happens?

  • If test fails → Playwright says “OK, expected!”
  • If test passes → Playwright says “Wait, this should fail!”

4. test.fixme() - The “Under Construction” Sign 🔧

What Is It?

test.fixme() tells Playwright: “Skip this test. Someone needs to fix it later.”

It’s like putting up an “Under Construction” sign—the road is closed, and we need workers to fix it.

When Do We Use It?

  • The test is incomplete
  • You found a problem but can’t fix it now
  • Marking tests for future attention

Simple Example

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

test.fixme('user profile editing', async ({ page }) => {
  // TODO: Complete this test
  // The profile page needs refactoring
});

Fixme With Condition

test('video player', async ({ page, browserName }) => {
  test.fixme(browserName === 'webkit',
    'Video controls broken in Safari');
  await page.click('#play-video');
});

Difference from skip()

test.skip() test.fixme()
“Don’t run this” “Don’t run this AND fix it later”
For intentional skipping For incomplete/broken tests
Permanent or temporary Always signals “needs work”

5. test.slow() - The “School Zone” Sign 🐢

What Is It?

test.slow() tells Playwright: “This test needs more time. Give it 3x the normal timeout.”

It’s like a school zone—cars slow down because kids might be crossing. Some tests need extra patience!

When Do We Use It?

  • Tests with lots of data loading
  • Tests that involve file uploads/downloads
  • Tests running on slow environments

Simple Example

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

test.slow('upload large file', async ({ page }) => {
  // Gets 3x more time to complete
  await page.setInputFiles('#upload', 'huge-file.zip');
  await page.click('#submit');
});

Slow With Condition

test('data export', async ({ page, browserName }) => {
  test.slow(browserName === 'webkit',
    'Safari is slower with exports');
  await page.click('#export-all-data');
});

How It Works

Normal timeout: 30 seconds
With test.slow(): 90 seconds (30 × 3)

6. Test Tags - The “Road Labels” 🏷️

What Are They?

Test tags are like labels on roads—“Highway”, “Local Road”, “Scenic Route”. They help you organize and run specific groups of tests.

How to Add Tags

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

test('quick login check',
  { tag: '@smoke' },
  async ({ page }) => {
    await page.goto('/login');
});

test('full checkout flow',
  { tag: ['@slow', '@e2e'] },
  async ({ page }) => {
    // Complex test with multiple tags
});

test('admin panel access',
  { tag: '@admin' },
  async ({ page }) => {
    await page.goto('/admin');
});

Running Tests by Tag

# Run only smoke tests
npx playwright test --grep @smoke

# Run slow tests
npx playwright test --grep @slow

# Run everything EXCEPT admin tests
npx playwright test --grep-invert @admin

Common Tag Ideas

Tag Purpose
@smoke Quick sanity checks
@slow Long-running tests
@e2e End-to-end flows
@api API-related tests
@mobile Mobile-specific tests
@critical Must-pass tests

7. Conditional Test Execution - The “Smart Traffic Light” 🧠

What Is It?

Run or skip tests based on conditions—like a smart traffic light that changes based on traffic patterns.

Skip Based on Browser

test('copy to clipboard', async ({ page, browserName }) => {
  test.skip(browserName === 'firefox',
    'Clipboard API differs in Firefox');
  await page.click('#copy-btn');
});

Skip Based on Device

test('hover tooltip', async ({ page, isMobile }) => {
  test.skip(isMobile, 'No hover on mobile');
  await page.hover('#info-icon');
});

Skip Based on Environment

test('production smoke test', async ({ page }) => {
  test.skip(process.env.ENV !== 'production',
    'Only runs in production');
  await page.goto('https://mysite.com');
});

Using test.info()

test('platform-specific test', async ({ page }) => {
  const info = test.info();

  if (info.project.name === 'Mobile Safari') {
    test.skip(true, 'Not supported on iOS');
  }

  await page.click('.desktop-only-feature');
});

Quick Visual Summary

graph LR A["Test Control Methods"] --> B["test.skip"] A --> C["test.only"] A --> D["test.fail"] A --> E["test.fixme"] A --> F["test.slow"] A --> G["Test Tags"] A --> H["Conditional Execution"] B --> B1[Don't run this test] C --> C1["Run ONLY this test"] D --> D1["Expect this to fail"] E --> E1["Skip + needs fixing"] F --> F1["3x timeout"] G --> G1["Organize & filter"] H --> H1["Smart conditions"]

Real-World Example: E-Commerce Test Suite

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

// Smoke test - always run
test('homepage loads',
  { tag: '@smoke' },
  async ({ page }) => {
    await page.goto('/');
    await expect(page).toHaveTitle(/Shop/);
});

// Skip on mobile - no hover
test('product hover preview', async ({ page, isMobile }) => {
  test.skip(isMobile, 'No hover on mobile');
  await page.hover('.product-card');
  await expect(page.locator('.preview')).toBeVisible();
});

// Known bug - expected to fail
test.fail('discount code bug', async ({ page }) => {
  await page.fill('#discount', 'SAVE20');
  await page.click('#apply');
  // Bug: Discount not applying correctly
});

// Slow test - needs extra time
test.slow('full checkout with payment', async ({ page }) => {
  await page.goto('/cart');
  await page.click('#checkout');
  await page.fill('#card', '4242424242424242');
  // ... more steps
});

// Fix later - incomplete test
test.fixme('wishlist sync', async ({ page }) => {
  // TODO: Implement after API is ready
});

Remember! 🎯

Method Traffic Analogy Use When
test.skip() Road Closed Feature not ready
test.only() VIP Lane Debugging one test
test.fail() Expected Pothole Known bug exists
test.fixme() Under Construction Test needs work
test.slow() School Zone Test needs more time
Tags Road Labels Organizing tests
Conditional Smart Traffic Light Browser/device specific

Now you’re ready to control your tests like a pro traffic controller! 🚦✨

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.