process.env.VARIABLE retorna string | undefined no TypeScript, então um teste que usa uma credencial ausente não falha com um erro claro na inicialização. A falha acontece no meio do teste com um erro críptico de locator ou autenticação. A correção é um wrapper requireEnv() que lança erro na inicialização com o nome da variável. Uma variável ausente como TEST_USER_EMAIL para a suite antes do primeiro teste rodar e diz exatamente o que configurar.

O que fica onde

No playwright.config.ts (commitado no source control):

export default defineConfig({
  timeout: 30000,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 4 : undefined,
  reporter: [['html'], ['list']],
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
});

Em arquivos .env ou secrets do CI (nunca commitados para secrets):

BASE_URL=https://staging.myapp.com
API_KEY=sk-test-abc123
TEST_USER_EMAIL=testuser@example.com
TEST_USER_PASSWORD=SecurePass123!
DATABASE_URL=postgresql://localhost:5432/testdb

Configurando o dotenv

npm install -D dotenv

Crie .env com valores padrão seguros (sem credenciais reais):

# .env
BASE_URL=http://localhost:3000
TEST_USER_EMAIL=test@example.com
TEST_USER_PASSWORD=
DATABASE_URL=

Crie .env.local com valores locais reais (no .gitignore):

# .env.local — nunca commitado
TEST_USER_EMAIL=realtest@example.com
TEST_USER_PASSWORD=RealPassword123
DATABASE_URL=postgresql://localhost:5432/myapp_test

Carregue no config:

// playwright.config.ts
import dotenv from 'dotenv';
import path from 'path';

// Carrega .env.local primeiro (sobrescreve .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 ao .gitignore:

.env.local
playwright/.auth/
test-results/
playwright-report/

Variáveis de ambiente com tipagem segura

process.env.VARIABLE retorna string | undefined. Capturar variáveis ausentes no runtime do teste é ruim. Capture na inicialização:

// utils/env.ts
function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) {
    throw new Error(`Variável de ambiente ${name} é obrigatória mas não está definida`);
  }
  return value;
}

export const env = {
  baseURL: process.env.BASE_URL || 'http://localhost:3000',
  apiKey: requireEnv('API_KEY'),
  testUser: {
    email: requireEnv('TEST_USER_EMAIL'),
    password: requireEnv('TEST_USER_PASSWORD'),
  },
};

Use nos testes:

import { env } from '../utils/env';

test('login com credenciais de teste', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill(env.testUser.email);
  await page.getByLabel('Password').fill(env.testUser.password);
});

Se TEST_USER_EMAIL não estiver definido, você recebe um erro claro na inicialização: Variável de ambiente TEST_USER_EMAIL é obrigatória mas não está definida. Não uma falha críptica no meio do teste.

Configuração por projeto

Para múltiplos ambientes de teste, use projetos do Playwright com configurações diferentes:

// playwright.config.ts
const environments = {
  local: {
    baseURL: 'http://localhost:3000',
    apiURL: 'http://localhost:8000',
  },
  staging: {
    baseURL: 'https://staging.myapp.com',
    apiURL: 'https://api-staging.myapp.com',
  },
  production: {
    baseURL: 'https://myapp.com',
    apiURL: 'https://api.myapp.com',
  },
};

const currentEnv = (process.env.TEST_ENV as keyof typeof environments) || 'local';
const config = environments[currentEnv];

export default defineConfig({
  use: {
    baseURL: config.baseURL,
    extraHTTPHeaders: {
      'X-API-Base': config.apiURL,
    },
  },
});

Para rodar:

TEST_ENV=staging npx playwright test
TEST_ENV=production npx playwright test --grep @smoke

Feature flags em testes

Quando seu app usa feature flags, os testes podem precisar se comportar diferente com base no que está habilitado:

// Verifica se uma feature está habilitada via variável de ambiente
const NEW_CHECKOUT = process.env.FEATURE_NEW_CHECKOUT === 'true';

test('fluxo de checkout', async ({ page }) => {
  await page.goto('/checkout');

  if (NEW_CHECKOUT) {
    // Testa a nova UI de checkout
    await expect(page.getByTestId('new-checkout-form')).toBeVisible();
  } else {
    // Testa a UI de checkout legada
    await expect(page.getByTestId('legacy-checkout-form')).toBeVisible();
  }
});

Melhor ainda: arquivos de teste separados por estado de feature flag, controlados por projetos do Playwright:

projects: [
  {
    name: 'new-checkout',
    testMatch: '**/checkout-new/**',
    use: { extraHTTPHeaders: { 'X-Feature-New-Checkout': 'true' } },
  },
  {
    name: 'legacy-checkout',
    testMatch: '**/checkout-legacy/**',
  },
],

Configuração de ambiente no CI/CD

Exemplo com GitHub Actions e tratamento correto de secrets:

# .github/workflows/tests.yml
env:
  # Não sensível — usa vars, visível nos logs
  BASE_URL: ${{ vars.STAGING_URL }}
  TEST_ENV: staging

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run tests
        env:
          # Sensível — usa secrets, mascarado nos logs
          TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
          TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
          API_KEY: ${{ secrets.API_KEY }}
        run: npx playwright test

A distinção importante:

  • vars.*: configuração não sensível, visível nos logs do workflow
  • secrets.*: mascarado nos logs, nunca impresso, para credenciais e chaves

Validando a configuração antes da suite rodar

Use o global setup para validar o ambiente antes dos testes executarem:

// global-setup.ts
export default async function globalSetup() {
  const required = ['TEST_USER_EMAIL', 'TEST_USER_PASSWORD', 'BASE_URL'];
  const missing = required.filter(key => !process.env[key]);

  if (missing.length > 0) {
    throw new Error(
      `Variáveis de ambiente obrigatórias ausentes:\n${missing.map(k => `  - ${k}`).join('\n')}\n\n` +
      `Copie .env.example para .env.local e preencha os valores.`
    );
  }

  console.log(`Rodando testes contra: ${process.env.BASE_URL}`);
}

Isso falha rápido com uma mensagem clara em vez de deixar o primeiro teste falhar com um erro confuso de locator.

→ Veja também: Arquivo de Configuração do Playwright Explicado: Todas as Opções | Configuração de Ambiente no Playwright: Local, Staging e Produção | GitHub Actions para Testes Playwright: A Configuração Completa (2026)