Параметризованные тесты позволяют запускать один и тот же набор тестов с различными наборами данных, что значительно упрощает поддержку и расширяемость автоматизированного тестирования. В Protractor, как и в других инструментах на базе Jasmine или Mocha, этот подход реализуется с помощью массивов данных и циклического создания спецификаций.
Основная идея параметризации заключается в хранении входных данных и ожидаемых результатов в структурах данных (массивы, объекты), а затем прохождении этих данных циклом. Пример структуры:
const testData = [
{ username: 'user1', password: 'pass1', expectedTitle: 'Dashboard' },
{ username: 'user2', password: 'pass2', expectedTitle: 'Dashboard' },
{ username: 'invalid', password: 'wrong', expectedTitle: 'Login Failed' }
];
Каждый элемент массива представляет отдельный набор параметров для теста.
forEachДля создания отдельных тестов на основе данных массива часто
используется метод forEach. Это обеспечивает независимость
каждого запускаемого сценария:
testData.forEach(data => {
it(`should login with username: ${data.username}`, async () => {
await browser.get('https://example.com/login');
await element(by.id('username')).sendKeys(data.username);
await element(by.id('password')).sendKeys(data.password);
await element(by.id('loginButton')).click();
const title = await browser.getTitle();
expect(title).toEqual(data.expectedTitle);
});
});
В данном примере каждый объект массива создаёт отдельный
it блок с соответствующими параметрами. Это облегчает
диагностику ошибок, так как Jasmine отображает имя теста, включая
параметры.
data-driven подхода с
jasmine-data-providerProtractor поддерживает интеграцию с внешними библиотеками для
параметризации. Одна из популярных — jasmine-data-provider.
Она позволяет более структурировано определять наборы данных и
автоматически генерировать тесты:
const using = require('jasmine-data-provider');
function loginData() {
return [
{ username: 'user1', password: 'pass1', expectedTitle: 'Dashboard' },
{ username: 'user2', password: 'pass2', expectedTitle: 'Dashboard' },
{ username: 'invalid', password: 'wrong', expectedTitle: 'Login Failed' }
];
}
using(loginData, (data) => {
it(`should login with username: ${data.username}`, async () => {
await browser.get('https://example.com/login');
await element(by.id('username')).sendKeys(data.username);
await element(by.id('password')).sendKeys(data.password);
await element(by.id('loginButton')).click();
const title = await browser.getTitle();
expect(title).toEqual(data.expectedTitle);
});
});
Такой подход повышает читаемость и поддержку тестов, особенно при большом количестве комбинаций данных.
beforeEachИногда требуется подготовка данных перед каждым тестом, особенно если
входные данные зависят от состояния приложения. Для этого используется
beforeEach:
let currentData;
beforeEach(() => {
currentData = testData.shift(); // получение следующего набора данных
});
it(`should login with username: ${currentData.username}`, async () => {
await browser.get('https://example.com/login');
await element(by.id('username')).sendKeys(currentData.username);
await element(by.id('password')).sendKeys(currentData.password);
await element(by.id('loginButton')).click();
const title = await browser.getTitle();
expect(title).toEqual(currentData.expectedTitle);
});
Использование beforeEach позволяет избежать повторного
дублирования кода подготовки данных внутри каждого it.
Для сложных сценариев, где набор параметров включает вложенные объекты или несколько этапов взаимодействия, применяются комбинированные массивы данных. Например, тестирование корзины интернет-магазина:
const cartData = [
{ items: [{name: 'Apple', qty: 2}, {name: 'Banana', qty: 3}], expectedTotal: 5 },
{ items: [{name: 'Orange', qty: 1}, {name: 'Grapes', qty: 4}], expectedTotal: 5 }
];
cartData.forEach(cart => {
it(`should calculate correct total for items: ${cart.items.map(i => i.name).join(', ')}`, async () => {
await browser.get('https://example.com/cart');
for (const item of cart.items) {
const input = element(by.id(`${item.name}-qty`));
await input.clear();
await input.sendKeys(item.qty);
}
await element(by.id('calculateTotal')).click();
const total = await element(by.id('total')).getText();
expect(parseInt(total)).toEqual(cart.expectedTotal);
});
});
Такой подход позволяет легко расширять тесты, добавляя новые сценарии и комбинации данных без изменения основной логики теста.
Параметризованные тесты в Protractor являются основой для устойчивого и масштабируемого тестирования веб-приложений, особенно при работе с динамическими интерфейсами и множеством комбинаций входных данных.