Hardcodear baseURL en playwright.config.ts significa que cambiar de entorno requiere editar el archivo. Alguien se olvida de revertirlo, los tests corren contra el entorno equivocado, y todo pasa silenciosamente contra datos desactualizados. Este artículo cubre cómo leer la base URL y las credenciales desde variables de entorno, cargar archivos .env en local, pasar secretos a través de GitHub Actions, y envolver las credenciales en fixtures para que una variable faltante falle en la configuración en lugar de a mitad del test.

El problema con las URLs hardcodeadas

// Mal: hardcodeado, sin flexibilidad
use: {
  baseURL: 'https://staging.miapp.com',
},

Cuando necesitas correr en local: cambias la URL manualmente. Cuando CI necesita correr contra un deploy de feature branch: alguien se acuerda de revertirlo. Alguien se olvida. Los tests corren contra el entorno equivocado. El error es confuso porque todo pasa, solo que contra datos desactualizados.

Variables de entorno: el enfoque correcto

Lee la base URL desde una variable de entorno:

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

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

Ejecuta los tests:

# Local
npx playwright test

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

# Producción (solo smoke tests)
BASE_URL=https://miapp.com npx playwright test --grep @smoke

Sin cambios de código entre entornos. Solo una variable de entorno diferente.

Usar dotenv para desarrollo local

Repetir BASE_URL=... cada vez es tedioso. Usa un archivo .env:

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

Cárgalo en tu configuración:

npm install -D dotenv

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

// Cargar .env.local si existe, luego caer en .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',
  },
});

Agrega .env.local al .gitignore. Commitea .env con valores seguros por defecto (sin secretos). Los miembros del equipo crean su propio .env.local para las credenciales locales.

Configuraciones de múltiples entornos

Para proyectos con configuraciones significativamente diferentes por entorno, usá archivos de configuración separados:

playwright.config.ts           # Config base, ajustes compartidos
playwright.config.staging.ts   # Overrides para staging
playwright.config.prod.ts      # Overrides para producción (solo 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.miapp.com',
    video: 'on-first-retry',
  },
  retries: 2,
});

Ejecuta con:

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

Este patrón funciona bien para pipelines de CI donde distintas etapas necesitan diferentes cantidades de reintentos, configuración de video o configuraciones de reporter.

Acceder a variables de entorno en los tests

No pases variables de entorno directamente en los archivos de test; eso acopla la lógica del test a la infraestructura. Usa 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 y TEST_USER_PASSWORD deben estar configurados');
    }

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

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

Los tests usan el fixture, no process.env directamente:

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

test('el usuario puede iniciar sesión', 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: 'Iniciar sesión' }).click();
  await expect(page).toHaveURL('/dashboard');
});

El beneficio: si falta la variable de entorno, obtenés un error claro a nivel del fixture en lugar de un fallo confuso a mitad del test.

Variables de entorno en CI/CD

En GitHub Actions, configurá los secretos en los ajustes del repositorio y luego pasalos a los tests:

# .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

Usa vars (no secrets) para valores no sensibles como la URL de staging: son visibles en los logs de CI, lo que facilita el debugging. Usá secrets para credenciales, tokens y contraseñas.

Datos de test específicos por entorno

Algunos tests solo deberían ejecutarse en ciertos entornos. Usa tags y verificaciones de entorno:

test('smoke test del flujo de pago', {
  tag: '@smoke',
  annotation: { type: 'env', description: 'solo staging y producción' },
}, async ({ page }) => {
  // Saltear en desarrollo local para no impactar el sandbox de pagos
  test.skip(
    process.env.BASE_URL?.includes('localhost') === true,
    'Los tests de pago requieren entorno de staging o producción'
  );

  // cuerpo del test...
});

También puedes usar un fixture que resuelva el entorno actual:

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

Esto evita verificaciones if (process.env.BASE_URL === '...') hardcodeadas dispersas por los archivos de test.

Qué va en variables de entorno vs en el archivo de configuración

| Configuración | Dónde va |

|---|---|

| Base URL | Variable de entorno |

| Tokens de API, contraseñas | Variable de entorno (secreto) |

| Tipo de navegador | Archivo de configuración |

| Cantidad de reintentos | Archivo de configuración (por entorno si es necesario) |

| Valores de timeout | Archivo de configuración |

| Seeds de datos de test | Fixtures |

| Feature flags para comportamiento de tests | Variable de entorno |

La regla: todo lo que difiere entre entornos es una variable de entorno. Todo lo que es un ajuste estable del framework de testing va en el archivo de configuración.

→ See also: Archivo de Configuración de Playwright Explicado: Todas las Opciones | Variables de Entorno en Tests de Playwright: Una Guía Completa | GitHub Actions para Tests de Playwright: La Configuración Completa (2026)