Las entrevistas de Playwright evalúan criterio, no vocabulario: un entrevistador que escucha "getByRole es mejor que los selectores CSS" va a preguntar por qué, y "es más legible" no es una respuesta suficiente. Saber que los locators de getByRole se rompen cuando cambia el nombre accesible, no cuando cambia la clase CSS, es el tipo de profundidad que separa una respuesta junior de una senior. Esta guía cubre las preguntas que aparecen con más frecuencia en entrevistas, agrupadas por tema, con qué incluye una buena respuesta y dónde falla la mayoría de los candidatos.
Conceptos fundamentales
¿Qué es Playwright y en qué se diferencia de Selenium?Playwright es un framework de automatización de tests para aplicaciones web construido por Microsoft. Las diferencias arquitecturales clave respecto a Selenium:
Auto-espera: Playwright espera a que los elementos sean accionables antes de interactuar con ellos, sin necesidad de WebDriverWait ni ExpectedConditions. Selenium requiere código de espera explícita.
Todo integrado: Playwright incluye test runner, librería de aserciones, trace viewer y reporter HTML. Selenium es una librería de automatización del navegador, así que el resto lo armas tú.
Protocolo del navegador: Playwright usa CDP (Chrome DevTools Protocol) para Chromium y protocolos similares para Firefox y WebKit. Selenium usa el protocolo WebDriver, que es más lento por los viajes de ida y vuelta adicionales en la red.
Diseño centrado en JavaScript: La API de JavaScript/TypeScript de Playwright usa async/await nativo. Los bindings de JS de Selenium se agregaron después de la implementación en Java y se nota.
Antes de ejecutar una acción, Playwright verifica que el elemento objetivo cumpla un conjunto de condiciones de "accionabilidad". Para click(): el elemento debe ser visible, estar en el viewport, no estar cubierto por otro elemento, estar habilitado y ser estable (sin moverse por animaciones). Para fill(): el elemento debe ser visible y editable.
Estas verificaciones corren automáticamente en un bucle de polling hasta que pasan o se alcanza el timeout. Puedes ver la verificación de accionabilidad en el mensaje de error cuando falla un test. Te dice exactamente qué condición no se cumplió.
¿Cuál es la diferencia entrepage.locator() y los métodos getBy*?
page.locator() acepta un selector CSS, XPath o una cadena de texto. Es un locator de propósito general.
Los métodos getBy* (getByRole, getByLabel, getByText, getByTestId, etc.) son locators semánticos. Encuentran elementos de la misma manera que un usuario los identificaría, usando atributos de accesibilidad en lugar de estructura CSS.
Los métodos getBy* son preferidos porque son más resistentes a cambios en la UI: renombrar una clase CSS no los rompe, pero cambiar el nombre accesible de un botón sí, que es exactamente el tipo de cambio que afecta a los usuarios reales.
Los fixtures son el sistema de inyección de dependencias de Playwright. Proveen objetos pre-construidos a los tests sin requerir setup manual en cada uno. Los fixtures integrados son: page (una página del navegador), context (un contexto del navegador), browser (la instancia del navegador), request (un cliente HTTP) y browserName.
Los fixtures personalizados los extienden:
const test = base.extend({
loggedInPage: async ({ page }, use) => {
await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
await use(page); // entrega la página con sesión al test
}
});
test('el dashboard muestra ítems', async ({ loggedInPage }) => {
await expect(loggedInPage.getByText('My Travel Items')).toBeVisible();
});Cada test de Playwright corre en su propio contexto del navegador por defecto: una pizarra limpia sin cookies, sin localStorage, sin datos cacheados de otros tests. Esto significa que los tests no se interfieren entre sí aunque corran en paralelo.
Puedes compartir un contexto entre tests dentro de un bloque test.describe usando test.describe.configure({ mode: 'serial' }), pero esto reduce la aislación y debería usarse con moderación.
Locators y aserciones
¿Cuándo usaríasgetByTestId en lugar de getByRole?
getByRole es preferido cuando el elemento tiene un rol ARIA significativo y un nombre accesible. getByTestId es útil cuando el elemento no tiene un rol semántico claro (un componente personalizado que renderiza como un div genérico), cuando el nombre accesible es dinámico o no determinístico, o cuando los desarrolladores agregaron explícitamente atributos data-testid como anclajes estables para tests.
La regla práctica: usa getByRole primero, cae a getByTestId cuando getByRole no funciona limpiamente.
expect(locator).toBeVisible()?
Verifica que el elemento está presente en el DOM, no está oculto mediante CSS (display: none, visibility: hidden, opacity: 0), y tiene dimensiones distintas de cero. Un elemento con display: none no pasa esta verificación. Un elemento fuera del viewport pero no oculto sí pasa.
toBeVisible() también hace reintento automático. Sigue verificando hasta que la aserción pasa o el timeout expira. Eso es diferente a una simple verificación booleana.
¿Cómo asertás sobre múltiples elementos a la vez?
// Verificar conteo
await expect(page.getByRole('row')).toHaveCount(6);
// Verificar que todos los ítems de una lista contienen texto
const items = page.getByRole('listitem');
await expect(items).toContainText(['Tokio', 'París', 'Oslo']);
// Verificar el texto de cada ítem
await expect(items).toHaveText(['Tokio', 'París', 'Oslo']); // coincidencia exactapage.waitForSelector() y usar una aserción de locator?
page.waitForSelector() es una API de más bajo nivel que espera a que el elemento aparezca en el DOM. Es anterior a la API de locators de Playwright.
Las aserciones de locator como await expect(locator).toBeVisible() son el enfoque moderno. Son más expresivas, hacen reintento automático, y funcionan consistentemente con el sistema de locators de Playwright. Usa aserciones en los tests, no waitForSelector.
Red y API
¿Cómo interceptás una petición de red en Playwright?// Interceptar y modificar una respuesta
await page.route('**/api/items', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, destination: 'Ciudad Mockeada' }])
});
});
// Bloquear peticiones específicas
await page.route('**/*.png', route => route.abort());
// Modificar headers de la petición
await page.route('**/api/**', route => {
route.continue({
headers: { ...route.request().headers(), 'X-Custom-Header': 'test' }
});
});page.request y el fixture request?
page.request comparte el contexto del navegador. Envía peticiones con las mismas cookies y estado de autenticación que la página del navegador actual.
El fixture request es un cliente HTTP independiente sin sesión del navegador. Usa request para tests de API puros que no necesitan estado del navegador. Usa page.request cuando necesitas hacer llamadas a la API con la sesión del usuario actualmente autenticado.
// Esperar una petición y respuesta específica
const responsePromise = page.waitForResponse('**/api/items');
await page.getByRole('button', { name: 'Actualizar' }).click();
const response = await responsePromise;
expect(response.status()).toBe(200);Configuración y CI
¿Cómo corrés tests de Playwright en GitHub Actions?# .github/workflows/playwright.yml
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/Puntos clave: npm ci en lugar de npm install para reproducibilidad. npx playwright install --with-deps instala los navegadores y sus dependencias del sistema. if: always() en la subida del reporte garantiza que lo obtenés aunque los tests fallen.
El sharding divide tu suite de tests entre múltiples máquinas. Cada shard corre un subconjunto de tests:
npx playwright test --shard=1/4 # esta máquina corre el 25% de los tests
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4Úsalo cuando tu suite tarda más de 5 minutos en una sola máquina y querés feedback más rápido del CI. Los tests deben estar aislados (sin estado compartido) para que el sharding funcione correctamente.
¿Qué hacetrace: 'on-first-retry' en playwright.config.ts?
Cuando un test falla y se reintenta, Playwright graba una traza: una grabación completa de la ejecución del test que incluye snapshots del DOM, peticiones de red, screenshots en cada paso y logs de consola. La traza se guarda en la carpeta de resultados de tests y es visible con npx playwright show-report o subiéndola a trace.playwright.dev.
Arquitectura y patrones
¿Cuándo usarías Page Object Model versus tests simples?Usa tests simples cuando: tenés un número pequeño de tests (menos de 20), los tests son exploratorios, o estás recién empezando.
Usa POM cuando: múltiples tests interactúan con la misma página, los locators se repiten en archivos de test, o la UI cambia frecuentemente y querés actualizar los locators en un solo lugar.
El disparador práctico: cuando te encontrás copiando las mismas 5+ líneas entre tests, es momento de crear un page object.
¿Cómo manejás la autenticación entre tests sin iniciar sesión en cada uno?storageState guarda y restaura las cookies y localStorage del navegador. Corrés el login una vez en un archivo de global setup, guardás el estado, y configurás los tests para cargarlo:
// global-setup.ts
import { chromium } from '@playwright/test';
export default async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
await page.context().storageState({ path: 'auth.json' });
await browser.close();
}// playwright.config.ts
export default defineConfig({
globalSetup: './global-setup.ts',
use: { storageState: 'auth.json' }
});En orden: variables de entorno (secretos faltantes en CI), base URL (localhost hardcodeado), timing (los runners de CI son más lentos, agregá retries: 1), await faltante, diferencias de versión del navegador. Descargá el artefacto de traza del CI y compará la ejecución paso a paso con la traza local.
PWDEBUG=1 npx playwright test abre el inspector con ejecución paso a paso. Úsalo para entender qué está haciendo realmente Playwright antes de asumir que hay un bug.FAQ
¿Debería conocer los internos de Playwright para una entrevista senior?Entender el protocolo CDP, cómo está implementada la auto-espera, y cómo funcionan los fixtures por debajo muestra profundidad. No te van a pedir que los implementes, pero explicar "la auto-espera hace polling de las verificaciones de accionabilidad en un timer con backoff exponencial" impresiona más que "espera automáticamente."
¿Cómo demuestro experiencia en Playwright si no lo usé profesionalmente?Un repo de GitHub con 20+ tests que cubren el login de una aplicación real, operaciones CRUD y llamadas a API es evidencia concreta. lab.becomeqa.com está diseñado específicamente para esto. Recórrele al entrevistador tu estructura de tests y explica por qué tomaste las decisiones arquitecturales que tomaste.