Factory паттерн для тестовых объектов

Playwright позволяет эффективно тестировать веб-приложения с использованием автоматизированных сценариев. Однако, при сложных тестах, часто возникает необходимость создавать или управлять различными объектами, такими как страницы, формы, пользователи или данные. В таких случаях применение Factory паттерна может значительно упростить и улучшить организацию тестового кода.

Что такое Factory паттерн?

Factory паттерн является порождающим паттерном проектирования, который предоставляет интерфейс для создания объектов, не указывая точный класс создаваемого объекта. В контексте автоматизированного тестирования это позволяет создавать тестовые данные и объекты с минимальными усилиями и высокой гибкостью. Вместо того чтобы создавать множество однотипных объектов вручную, можно определить фабрику, которая будет создавать их по необходимости.

Преимущества использования Factory паттерна

  1. Изоляция логики создания объектов. Factory позволяет сосредоточить всю логику создания тестовых объектов в одном месте, что значительно упрощает поддержку кода.
  2. Повторное использование. Одна и та же фабрика может использоваться в разных тестах для создания однотипных объектов.
  3. Гибкость. Паттерн позволяет легко изменять способ создания объектов или их параметры, не изменяя код самих тестов.
  4. Упрощение тестов. Тесты становятся проще и чище, так как не нужно повторно создавать объекты в каждом тесте.

Пример использования Factory паттерна для создания тестовых объектов

В Playwright часто бывает необходимо работать с веб-страницами, элементами или данными формы. Вместо того чтобы в каждом тесте вручную создавать объекты страницы, лучше использовать фабрики для их создания.

Пример фабрики для страницы, которая генерирует объекты LoginPage:

class LoginPage {
  constructor(page) {
    this.page = page;
    this.usernameInput = page.locator('input[name="username"]');
    this.passwordInput = page.locator('input[name="password"]');
    this.submitButton = page.locator('button[type="submit"]');
  }

  async login(username, password) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }
}

class LoginPageFactory {
  static create(page) {
    return new LoginPage(page);
  }
}

module.exports = { LoginPageFactory };

В этом примере фабрика LoginPageFactory отвечает за создание объекта страницы LoginPage, инкапсулируя в себе все детали реализации. Это упрощает создание тестов, так как теперь можно просто использовать фабрику:

const { test, expect } = require('@playwright/test');
const { LoginPageFactory } = require('./factories');

test('Login test', async ({ page }) => {
  const loginPage = LoginPageFactory.create(page);
  await loginPage.login('user123', 'password123');
  expect(await page.locator('h1').textContent()).toBe('Welcome');
});

Структура тестовых данных через Factory паттерн

Фабрики также могут быть полезны для создания тестовых данных, например, для пользователей. В реальных проектах часто нужно тестировать функциональность с различными данными, такими как имена, email, роли и т. д.

Пример фабрики для создания пользователей:

class User {
  constructor(username, email, role) {
    this.username = username;
    this.email = email;
    this.role = role;
  }
}

class UserFactory {
  static createAdmin() {
    return new User('admin', 'admin@example.com', 'admin');
  }

  static createUser() {
    return new User('user', 'user@example.com', 'user');
  }

  static createGuest() {
    return new User('guest', 'guest@example.com', 'guest');
  }
}

module.exports = { UserFactory };

Теперь в тестах можно использовать фабрику для создания различных типов пользователей:

const { UserFactory } = require('./factories');

test('Admin user test', async () => {
  const admin = UserFactory.createAdmin();
  // используйте admin в тестах
});

test('Guest user test', async () => {
  const guest = UserFactory.createGuest();
  // используйте guest в тестах
});

Использование Factory паттерна с данными из внешних источников

В некоторых случаях тестовые данные могут поступать из внешних источников, таких как базы данных, API или файлы. В этом случае фабрики могут быть использованы для инкапсуляции логики получения данных и создания объектов.

Пример фабрики, которая загружает тестовые данные из JSON файла:

const fs = require('fs');

class UserFactory {
  static createFromFile(filePath) {
    const data = JSON.parse(fs.readFileSync(filePath));
    return new User(data.username, data.email, data.role);
  }
}

module.exports = { UserFactory };

В этом примере фабрика загружает данные о пользователе из файла и создает объект User. Это полезно, если тесты должны работать с заранее подготовленными данными.

const { UserFactory } = require('./factories');

test('User from file', async () => {
  const user = UserFactory.createFromFile('test-data/user.json');
  // используйте user в тестах
});

Сложные фабрики для создания объектов с зависимостями

В более сложных сценариях тестирования может потребоваться создание объектов с зависимостями. Например, при тестировании функциональности корзины покупок могут быть необходимы одновременно объекты товаров и корзины. В таких случаях фабрики могут быть использованы для создания сложных объектов с зависимостями.

Пример фабрики для корзины покупок, которая зависит от объектов товаров:

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
}

class Cart {
  constructor() {
    this.products = [];
  }

  addProduct(product) {
    this.products.push(product);
  }

  getTotalPrice() {
    return this.products.reduce((total, product) => total + product.price, 0);
  }
}

class CartFactory {
  static createWithProducts(productsData) {
    const cart = new Cart();
    productsData.forEach((data) => {
      const product = new Product(data.name, data.price);
      cart.addProduct(product);
    });
    return cart;
  }
}

module.exports = { CartFactory };

В этом примере фабрика CartFactory принимает данные о товарах, создает объекты Product, добавляет их в корзину и возвращает готовый объект Cart.

const { CartFactory } = require('./factories');

test('Shopping cart test', async () => {
  const cart = CartFactory.createWithProducts([
    { name: 'Laptop', price: 1000 },
    { name: 'Headphones', price: 200 }
  ]);
  expect(cart.getTotalPrice()).toBe(1200);
});

Заключение

Использование Factory паттерна в тестировании с Playwright позволяет создавать гибкие и поддерживаемые тесты. Этот паттерн помогает управлять сложностью, изолировать логику создания объектов и улучшить повторное использование кода. С помощью фабрик можно легко создавать страницы, пользователей, товары, корзины и другие объекты, а также инкапсулировать логику получения и создания тестовых данных из внешних источников.