Colocar baseURL fixo no playwright.config.ts significa que trocar de ambiente exige editar o arquivo. Alguém esquece de reverter, os testes rodam contra o ambiente errado, e tudo passa silenciosamente com dados desatualizados.

O problema com URLs fixas

// Ruim — sem flexibilidade
use: {
  baseURL: 'https://staging.myapp.com',
},

Para rodar localmente: você muda a URL na mão. Para o CI rodar contra um deploy de feature branch: alguém lembra de mudar de volta. Alguém esquece. Os testes rodam contra o ambiente errado. O erro é confuso porque tudo passa, só que contra dados desatualizados.

Variáveis de ambiente: a abordagem correta

Leia a URL base de uma variável de ambiente:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
  },
});

Para rodar os testes:

# Local
npx playwright test

# Staging
BASE_URL=https://staging.myapp.com npx playwright test

# Produção (só smoke tests)
BASE_URL=https://myapp.com npx playwright test --grep @smoke

Nenhuma alteração de código entre ambientes. Só uma variável de ambiente diferente.

Usando dotenv no desenvolvimento local

Repetir BASE_URL=... toda vez é chato. Use um arquivo .env:

# .env.local
BASE_URL=http://localhost:3000
API_TOKEN=dev-token-abc123
TEST_USER_EMAIL=test@example.com
TEST_USER_PASSWORD=testpass123

Carregue no config:

npm install -D dotenv

// playwright.config.ts
import { defineConfig } from '@playwright/test';
import dotenv from 'dotenv';
import path from 'path';

// Carrega .env.local se existir, depois cai para .env
dotenv.config({ path: path.resolve(__dirname, '.env.local') });
dotenv.config({ path: path.resolve(__dirname, '.env') });

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
  },
});

Adicione .env.local ao .gitignore. Faça commit do .env com valores padrão seguros (sem secrets). Cada membro do time cria seu próprio .env.local para credenciais locais.

Configs por ambiente

Para projetos com configurações bem diferentes por ambiente, use arquivos de config separados:

playwright.config.ts           # Config base, configurações compartilhadas
playwright.config.staging.ts   # Overrides de staging
playwright.config.prod.ts      # Overrides de produção (só smoke)

// playwright.config.staging.ts
import { defineConfig } from '@playwright/test';
import baseConfig from './playwright.config';

export default defineConfig({
  ...baseConfig,
  use: {
    ...baseConfig.use,
    baseURL: 'https://staging.myapp.com',
    video: 'on-first-retry',
  },
  retries: 2,
});

Para rodar:

npx playwright test --config=playwright.config.staging.ts

Esse padrão funciona bem em pipelines de CI onde estágios diferentes precisam de contagens de retry, configurações de vídeo ou reporters distintos.

Acessando variáveis de ambiente nos testes

Não acesse variáveis de ambiente diretamente nos arquivos de teste: isso acopla a lógica do teste à infraestrutura. Use fixtures:

// fixtures/env.ts
import { test as base } from '@playwright/test';

type EnvFixtures = {
  testUser: { email: string; password: string };
  apiToken: string;
};

export const test = base.extend<EnvFixtures>({
  testUser: async ({}, use) => {
    const email = process.env.TEST_USER_EMAIL;
    const password = process.env.TEST_USER_PASSWORD;

    if (!email || !password) {
      throw new Error('TEST_USER_EMAIL and TEST_USER_PASSWORD must be set');
    }

    await use({ email, password });
  },

  apiToken: async ({}, use) => {
    const token = process.env.API_TOKEN;
    if (!token) throw new Error('API_TOKEN must be set');
    await use(token);
  },
});

Os testes usam a fixture, não process.env diretamente:

import { test } from '../fixtures/env';
import { expect } from '@playwright/test';

test('usuário consegue fazer login', async ({ page, testUser }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill(testUser.email);
  await page.getByLabel('Password').fill(testUser.password);
  await page.getByRole('button', { name: 'Log in' }).click();
  await expect(page).toHaveURL('/dashboard');
});

O benefício: se a variável de ambiente estiver faltando, você recebe um erro claro no nível da fixture, não uma falha confusa no meio do teste.

Variáveis de ambiente no CI/CD

No GitHub Actions, configure secrets nas configurações do repositório e passe para os testes:

# .github/workflows/tests.yml
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      BASE_URL: ${{ vars.STAGING_URL }}
      API_TOKEN: ${{ secrets.API_TOKEN }}
      TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
      TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test

Use vars (não secrets) para valores não sensíveis como a URL de staging: eles aparecem nos logs do CI, o que ajuda no debug. Use secrets para credenciais, tokens e senhas.

Dados de teste por ambiente

Alguns testes devem rodar só em certos ambientes. Use tags e verificações de ambiente:

test('smoke test do fluxo de pagamento', {
  tag: '@smoke',
  annotation: { type: 'env', description: 'staging e produção apenas' },
}, async ({ page }) => {
  // Pula no desenvolvimento local para não bater no sandbox de pagamento
  test.skip(
    process.env.BASE_URL?.includes('localhost') === true,
    'Testes de pagamento requerem ambiente de staging ou produção'
  );

  // corpo do teste...
});

Ou use uma fixture que resolve o ambiente atual:

const currentEnv = process.env.BASE_URL?.includes('staging')
  ? 'staging'
  : process.env.BASE_URL?.includes('localhost')
  ? 'local'
  : 'production';

Isso evita checks if (process.env.BASE_URL === '...') espalhados pelos arquivos de teste.

O que vai em variáveis de ambiente vs no config

| Configuração | Onde fica |

|---|---|

| URL base | Variável de ambiente |

| Tokens de API, senhas | Variável de ambiente (secret) |

| Tipo de navegador | Arquivo de config |

| Contagem de retries | Arquivo de config (por ambiente se necessário) |

| Valores de timeout | Arquivo de config |

| Seeds de dados de teste | Fixtures |

| Feature flags para comportamento de testes | Variável de ambiente |

A regra: tudo que muda entre ambientes vai como variável de ambiente. Tudo que é configuração estável do framework de testes fica no arquivo de config.

→ Veja também: Arquivo de Configuração do Playwright Explicado: Todas as Opções | Variáveis de Ambiente em Testes Playwright: Um Guia Completo | GitHub Actions para Testes Playwright: A Configuração Completa (2026)