El { page, request } en cada función de test de Playwright es destructuring: extrae esas propiedades del objeto fixture que el framework pasa automáticamente. Una vez que eso queda claro, los fixtures personalizados dejan de parecer magia y empiezan a verse como el mismo patrón repetido. Esta guía cubre el acceso a objetos, el destructuring con renombrado y valores por defecto, el destructuring anidado para respuestas de API, y el comportamiento de copia superficial del spread que hace que las mutaciones en datos de prueba se propaguen entre variantes.
Qué es un objeto
Un objeto es una colección de pares clave-valor. Las claves son strings; los valores pueden ser cualquier cosa:
const user = {
id: 1,
email: 'alice@example.com',
role: 'admin',
isActive: true,
};Accedes a los valores con notación de punto o de corchetes:
console.log(user.email); // 'alice@example.com'
console.log(user['role']); // 'admin'
console.log(user.id); // 1Modificas valores de la misma forma:
user.email = 'nuevaalice@example.com';
user.role = 'member';Objetos en datos de prueba
La mayoría de los datos de prueba en tests de Playwright se expresan como objetos o arrays de objetos:
const loginCredentials = {
email: 'qa_test@example.com',
password: 'PassValida123!',
};
await page.fill('[data-testid="email"]', loginCredentials.email);
await page.fill('[data-testid="password"]', loginCredentials.password);Colecciones de casos de prueba:
const EMAILS_INVALIDOS = [
{ input: '', descripcion: 'vacío' },
{ input: 'no-es-email', descripcion: 'falta el @' },
{ input: 'falta@', descripcion: 'falta el dominio' },
{ input: 'a@b', descripcion: 'sin TLD' },
];Cada EMAILS_INVALIDOS[0] es un objeto con las propiedades input y descripcion.
Destructuring: qué es
El destructuring permite extraer valores de un objeto (o array) en variables individuales en una sola línea, en lugar de múltiples líneas.
Sin destructuring
const user = { id: 1, name: 'Alice', email: 'alice@test.com', role: 'admin' };
const id = user.id;
const name = user.name;
const email = user.email;
const role = user.role;Con destructuring
const { id, name, email, role } = user;El mismo resultado: cuatro variables con los mismos valores, pero en una línea en lugar de cuatro.
Renombrar al hacer destructuring
Si quieres que la variable tenga un nombre diferente a la clave:
const config = {
database_host: 'localhost',
database_port: 5432,
};
const { database_host: host, database_port: port } = config;
console.log(host); // 'localhost'
console.log(port); // 5432Valores por defecto en el destructuring
Si una clave puede no existir, puedes proveer un valor por defecto:
const product = { name: 'Laptop', price: 999 };
const { name, price, discount = 0 } = product;
// discount = 0 (no estaba en product, usa el valor por defecto)Destructuring en parámetros de funciones
El lugar más común donde verás destructuring en Playwright es en las funciones de test:
// Sin destructuring
test('el usuario puede iniciar sesión', async (args) => {
const page = args.page;
const request = args.request;
// ...
});
// Con destructuring (patrón estándar de Playwright)
test('el usuario puede iniciar sesión', async ({ page, request }) => {
// page y request disponibles directamente
});Esto es destructuring de objetos en el parámetro de la función. La sintaxis { page, request } extrae esas propiedades del objeto fixture que Playwright pasa.
Por eso el código de tests de Playwright parece tener "variables mágicas": se están desestructurando del objeto fixture automáticamente.
Fixtures personalizados: destructuring en la práctica
Cuando creas fixtures personalizados, vas a escribir este patrón:
export const test = base.extend<{ loginPage: LoginPage }>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await use(loginPage);
},
});
// Luego en un test:
test('el login funciona', async ({ page, loginPage }) => {
// ^^^ desestructurado del objeto fixture
await loginPage.login('user@test.com', 'pass');
});Entender el destructuring hace que los fixtures tengan sentido.
Destructuring anidado
Los objetos pueden contener otros objetos. Podés desestructurar múltiples niveles a la vez:
const apiResponse = {
status: 200,
data: {
user: {
id: 123,
email: 'alice@test.com',
},
token: 'eyJhbGciOiJIUzI1NiJ9...',
},
};
// Destructuring anidado
const { status, data: { user: { id, email }, token } } = apiResponse;
console.log(status); // 200
console.log(id); // 123
console.log(email); // 'alice@test.com'
console.log(token); // 'eyJhbGciOiJIUzI1NiJ9...'Parece complejo al principio. En la práctica, raramente bajas más de 2 niveles. Si se complica, haz el destructuring en pasos:
const { data } = apiResponse;
const { user, token } = data;
const { id, email } = user;El mismo resultado, más fácil de leer.
Destructuring de arrays (brevemente)
Los arrays usan [] en lugar de {}:
const coordenadas = [40.7128, -74.0060];
const [latitud, longitud] = coordenadas;
// latitud = 40.7128, longitud = -74.0060Para saltear elementos, usa comas:
const [primero, , tercero] = ['a', 'b', 'c'];
// primero = 'a', tercero = 'c'El operador spread con objetos
El operador spread (...) copia todas las propiedades de un objeto en otro:
const baseUser = {
role: 'member',
isActive: true,
};
const adminUser = {
...baseUser, // copia role e isActive
email: 'admin@test.com',
role: 'admin', // sobreescribe el valor del spread
};
// { role: 'admin', isActive: true, email: 'admin@test.com' }En datos de prueba: crear variantes
const defaultUser = {
email: 'test@example.com',
password: 'PassValida1',
role: 'member',
isActive: true,
};
const adminUser = { ...defaultUser, role: 'admin' };
const inactiveUser = { ...defaultUser, isActive: false };
const customEmail = { ...defaultUser, email: 'custom@example.com' };Este patrón, "objeto base + sobrescrituras", es muy común en las fábricas de datos de prueba. Definís el estado válido por defecto una vez y lo extendés con spread para crear variantes.
Patrones prácticos en tests de Playwright
Destructuring del cuerpo de respuesta de API
const response = await request.post('/api/users', {
data: { email: 'nuevo@test.com', password: 'PassValida1' },
});
const { id, email, role, created_at } = await response.json();
expect(id).toBeTruthy();
expect(email).toBe('nuevo@test.com');
expect(role).toBe('member');Pasar parámetros de objeto a helpers
async function fillLoginForm(page: Page, { email, password }: { email: string; password: string }) {
await page.fill('[data-testid="email"]', email);
await page.fill('[data-testid="password"]', password);
}
// En el punto de llamada:
await fillLoginForm(page, { email: 'user@test.com', password: 'pass' });Combinar configuración de tests
const baseConfig = {
baseURL: 'https://lab.becomeqa.com',
timeout: 30000,
};
const ciConfig = {
...baseConfig,
timeout: 60000, // más lento en CI
video: 'retain-on-failure',
};Errores comunes
Hacer destructuring desde undefined
const response = await fetch('/api/user');
const { id } = await response.json(); // Si la respuesta es null/undefined, lanza un errorSiempre verifica que el valor exista antes de hacer destructuring en objetos profundamente anidados.
Modificar copias con spread no afecta al original (en el primer nivel)
const original = { name: 'Alice', data: { score: 100 } };
const copy = { ...original };
copy.name = 'Bob'; // original.name sigue siendo 'Alice'
copy.data.score = 200; // original.data.score también cambiaEl spread es una copia superficial. Los objetos anidados siguen siendo referencias compartidas.
Resumen
| Sintaxis | Qué hace |
|----------|---------|
| const { a, b } = obj | Extrae a y b de obj |
| const { a: x } = obj | Extrae a de obj, lo llama x |
| const { a = 5 } = obj | Extrae a, valor por defecto 5 si no existe |
| async ({ page, request }) => {} | Desestructura fixtures de Playwright |
| { ...obj, key: 'value' } | Extiende obj con spread, sobreescribe o agrega key |