Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção Já leu

O que é Playwright e Por Que Page Object Model? Playwright é um framework de automação de navegador mantido pela Microsoft que permite testes end-to-end em Chrome, Firefox e Safari simultaneamente. Diferente de outras ferramentas, oferece sincronização automática de elementos, suporte nativo a múltiplos contextos de navegador e debugging excepcional. O Page Object Model (POM) é um padrão arquitetural que encapsula a lógica de interação de cada página em classes dedicadas, separando testes da implementação de UI. Isso torna o código mais manutenível: quando um seletor muda, você altera apenas uma classe, não dezenas de testes. A combinação Playwright + POM cria uma base sólida para testes escaláveis. Em vez de espalhadores de e por todo seu código de teste, você cria métodos semanticamente ricos como que encapsulam a complexidade. Isso reduz duplicação, aumenta legibilidade e facilita refatorações quando o produto muda. Implementando Page Object Model com Playwright Estrutura Base de uma Page Object Cada página ou componente ganha sua

O que é Playwright e Por Que Page Object Model?

Playwright é um framework de automação de navegador mantido pela Microsoft que permite testes end-to-end em Chrome, Firefox e Safari simultaneamente. Diferente de outras ferramentas, oferece sincronização automática de elementos, suporte nativo a múltiplos contextos de navegador e debugging excepcional. O Page Object Model (POM) é um padrão arquitetural que encapsula a lógica de interação de cada página em classes dedicadas, separando testes da implementação de UI. Isso torna o código mais manutenível: quando um seletor muda, você altera apenas uma classe, não dezenas de testes.

A combinação Playwright + POM cria uma base sólida para testes escaláveis. Em vez de espalhadores de page.click() e page.fill() por todo seu código de teste, você cria métodos semanticamente ricos como loginUser(email, password) que encapsulam a complexidade. Isso reduz duplicação, aumenta legibilidade e facilita refatorações quando o produto muda.

Implementando Page Object Model com Playwright

Estrutura Base de uma Page Object

Cada página ou componente ganha sua própria classe. Aqui está um exemplo real:

// pages/LoginPage.js
export class LoginPage {
  constructor(page) {
    this.page = page;
    this.emailInput = page.locator('input[type="email"]');
    this.passwordInput = page.locator('input[type="password"]');
    this.loginButton = page.locator('button:has-text("Login")');
    this.errorMessage = page.locator('[data-testid="error-message"]');
  }

  async navigate() {
    await this.page.goto('https://app.example.com/login');
    await this.page.waitForLoadState('networkidle');
  }

  async login(email, password) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
    await this.page.waitForNavigation();
  }

  async getErrorText() {
    return await this.errorMessage.textContent();
  }

  async isErrorVisible() {
    return await this.errorMessage.isVisible();
  }
}

Usando Page Objects em Testes

Agora seus testes ficam limpos e legíveis:

// tests/auth.spec.js
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test.describe('Authentication', () => {
  let loginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.navigate();
  });

  test('should login successfully with valid credentials', async () => {
    await loginPage.login('user@example.com', 'password123');
    // Verificação aconteceria na próxima página
    expect(loginPage.page.url()).toContain('/dashboard');
  });

  test('should show error with invalid credentials', async () => {
    await loginPage.login('user@example.com', 'wrong');
    const isError = await loginPage.isErrorVisible();
    expect(isError).toBe(true);
  });
});

O POM torna o teste autoexplicativo: qualquer pessoa entende o fluxo sem conhecer HTML. Se o departamento de UX mudar o seletor do botão de login, você altera apenas LoginPage.js, não todos os 50 testes que usam aquela página.

CI/CD Integration com Playwright

Configuração GitHub Actions

Integrar Playwright em pipeline CI é trivial. Crie .github/workflows/tests.yml:

name: Playwright Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install dependencies
        run: npm install

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run tests
        run: npm run test:e2e

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Configuração Playwright e Package.json

No playwright.config.js, configure múltiplos projetos e relatórios:

// playwright.config.js
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html'],
    ['junit', { outputFile: 'test-results/junit.xml' }],
    ['github']
  ],

  use: {
    baseURL: 'https://app.example.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure'
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],

  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

No package.json:

{
  "scripts": {
    "test:e2e": "playwright test",
    "test:e2e:debug": "playwright test --debug",
    "test:e2e:headed": "playwright test --headed",
    "test:e2e:ui": "playwright test --ui"
  }
}

Relatórios e Artefatos

O Playwright gera automaticamente HTML reports. No CI, você captura como artefato (vide YAML acima). A flag --reporter=github integra resultados direto no Pull Request, mostrando quais testes falharam sem sair do GitHub.

Boas Práticas Avançadas

Isolation e Fixtures

Use fixtures para reutilizar page objects e gerenciar estado:

import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

export const test = base.extend({
  loginPage: async ({ page }, use) => {
    const login = new LoginPage(page);
    await login.navigate();
    await use(login);
    // Cleanup automático após teste
  },
});

test('authenticated flow', async ({ loginPage }) => {
  await loginPage.login('user@example.com', 'password123');
});

Seletores Resilientes

Evite seletores frágeis como div > div > button. Prefira:

// Bom: data-testid é controlado por você
this.submitButton = page.locator('[data-testid="submit-button"]');

// Melhor: role-based (acessível e semântico)
this.submitButton = page.locator('button:has-text("Submit")');

// Evite: índices e estrutura DOM
this.button = page.locator('form > div:nth-child(3) > button');

Conclusão

Testes end-to-end com Playwright ganham estrutura e manutenibilidade imediatas ao adotar Page Object Model. Você reduz duplicação, torna testes legíveis para toda a equipe e facilita refatorações quando a UI muda. A integração CI via GitHub Actions (ou GitLab, Jenkins, etc.) é simples: instale browsers, rode testes, capture relatórios. Comece pequeno com uma página, depois escale o padrão conforme a suite cresce. O investimento inicial em arquitetura sólida economiza semanas de manutenção futura.

Referências


Artigos relacionados