Playwright Codegen Explained: How to Use It Correctly and When NOT to Rely on It

I wasted hours writing bad locators until I discovered Playwright Codegen. Run one command, click through your app, and get working test code instantly.

But here is the truth most tutorials skip: Codegen is a starting point, not the final solution. Good engineers do not copy-paste blindly. They refine. They simplify. They make locators stable.

This guide shows you how to use Codegen correctly — and when to stop relying on it.

Contents

Getting Started with Codegen

# Launch Codegen against any URL
npx playwright codegen https://your-app.com

# With specific viewport
npx playwright codegen --viewport-size=1280,720 https://your-app.com

# With device emulation
npx playwright codegen --device="iPhone 13" https://your-app.com

Codegen opens a browser and a code panel. Every click, type, and navigation you perform gets recorded as test code in real time.

Understanding Codegen’s Locator Strategy

Codegen prioritizes locators in this order:

  1. getByRole() — most stable, based on ARIA roles
  2. getByLabel() — for form inputs with associated labels
  3. getByText() — matches visible text content
  4. getByTestId() — if data-testid attributes exist
  5. CSS/XPath selectors — last resort, most fragile

How to Refactor Codegen Output

Before Refactoring (Raw Codegen Output)

test('login flow', async ({ page }) => {
  await page.goto('https://app.example.com/login');
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.getByText('Welcome back').click();
});

After Refactoring (Production-Ready)

test('should login successfully with valid credentials', async ({ page }) => {
  // Arrange
  await page.goto('/login');
  
  // Act
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  
  // Assert
  await expect(page.getByText('Welcome back')).toBeVisible();
  await expect(page).toHaveURL(/dashboard/);
});

When NOT to Use Codegen

  • Complex assertions — Codegen records actions, not verifications. You must add assertions manually.
  • API-dependent flows — if test data needs API setup, Codegen cannot capture that.
  • Dynamic content — Codegen may generate fragile selectors for dynamically loaded elements.
  • Page Object patterns — Codegen outputs flat tests. Restructuring into Page Objects is always manual.

The Codegen Workflow for SDETs

  1. Record — Use Codegen to capture the basic flow
  2. Review — Check every locator for stability
  3. Refactor — Add assertions, extract Page Objects, parameterize data
  4. Harden — Replace fragile selectors, add waits for dynamic content
  5. Commit — Only commit the refactored version, never raw Codegen output

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.