Skip to content

Instantly share code, notes, and snippets.

@nadvolod
Created September 3, 2023 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nadvolod/da44ba1997fe44f737be66c431c6ef72 to your computer and use it in GitHub Desktop.
Save nadvolod/da44ba1997fe44f737be66c431c6ef72 to your computer and use it in GitHub Desktop.
Using a Data Factory with Playwright and TypeScript
interface UserData {
username: string;
password: string;
}
interface AddressData {
street: string;
city: string;
zipCode: string;
}
interface PaymentData {
cardNumber: string;
expiryDate: string;
cvv: string;
}
export class DataFactory {
static createUser(): UserData {
return {
username: `user_${Math.random().toString(36).substring(7)}`,
password: `pass_${Math.random().toString(36).substring(7)}`
};
}
static createAddress(): AddressData {
return {
street: `123${Math.random().toString(36).substring(7)} St`,
city: `City_${Math.random().toString(36).substring(7)}`,
zipCode: `ZIP_${Math.random().toString(36).substring(7)}`
};
}
static createPayment(): PaymentData {
return {
cardNumber: `4111${Math.floor(Math.random() * 9999999999)}`,
expiryDate: `0${Math.floor(Math.random() * 9) + 1}/23`,
cvv: `${Math.floor(Math.random() * 999)}`
};
}
}
Pros:
Separation of Concerns: Each factory focuses on creating a specific type of data, making the code more maintainable and understandable.
Reusability: You can use individual factories in different tests or even different projects.
Extensibility: Adding new types of data won't clutter a single factory class, reducing the risk of creating a "God object."
Cons:
Complexity: Handling multiple factories can become challenging, especially if they need to share data or configurations.
Dependency Management: If one factory relies on another, a change in one could affect the other.
Interaction Methods:
Composition: One factory can include an instance of another factory to generate composite data. Prefer composition over Inheritance.
Inheritance: If there's a common set of features among factories, a base factory class can be created.
Factory of Factories: Create a meta-factory that coordinates the use of multiple factories.
Example: Using Composition
Suppose you have UserDataFactory and AddressDataFactory. You can use them together in a CompleteUserDataFactory.
// UserDataFactory.ts
export class UserDataFactory {
generateUserData() {
return {
username: `user_${Math.random().toString(36).substring(7)}`,
password: `pass_${Math.random().toString(36).substring(7)}`
};
}
}
// AddressDataFactory.ts
export class AddressDataFactory {
generateAddressData() {
return {
street: `123${Math.random().toString(36).substring(7)} St`,
city: `City_${Math.random().toString(36).substring(7)}`,
zipCode: `ZIP_${Math.random().toString(36).substring(7)}`
};
}
}
// CompleteUserDataFactory.ts
import { UserDataFactory } from './UserDataFactory';
import { AddressDataFactory } from './AddressDataFactory';
export class CompleteUserDataFactory {
private userDataFactory = new UserDataFactory();
private addressDataFactory = new AddressDataFactory();
generateCompleteUserData() {
return {
user: this.userDataFactory.generateUserData(),
address: this.addressDataFactory.generateAddressData()
};
}
}
Summary:
Having multiple data factories makes sense when:
The types of data are logically separate.
You want to reuse a specific type of data across multiple tests or scenarios.
Interaction between them can be managed through techniques like Composition, Inheritance, or a Factory of Factories, depending on the complexity and the relationships between the data types.
//usage in a test
import { chromium } from 'playwright';
import { DataFactory } from './dataFactory';
(async () => {
const userData = DataFactory.createUser();
const addressData = DataFactory.createAddress();
const paymentData = DataFactory.createPayment();
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/login');
// Login
await page.fill('input[name="username"]', userData.username);
await page.fill('input[name="password"]', userData.password);
await page.click('button[type="submit"]');
await browser.close();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment