QA Interview Hub · Knowledge base · Quizzes

Patterns

Factory and data factories

A test data factory

A Factory encapsulates creating an object. In TypeScript tests the most popular variant is a simple function with defaults and a Partial override:

export function createCard(overrides: Partial<Card> = {}): Card {
  return {
    number: '4111111111111111',
    holder: 'TEST USER',
    expiry: '12/30',
    cvv: '123',
    ...overrides,
  };
}

const expired = createCard({ expiry: '01/20' });

This is shorter than a Builder and fully typed: TypeScript won't let you pass a field that isn't in Card.

The second popular form is a factory that not only builds an object but also creates the entity in the system through the API:

export async function createAccountViaApi(
  request: APIRequestContext,
  overrides: Partial<NewAccount> = {},
) {
  const res = await request.post('/api/accounts', {
    data: { currency: 'EUR', ...overrides },
  });
  expect(res.ok()).toBeTruthy();
  return (await res.json()) as Account;
}

Such a function is conveniently wrapped in a fixture, to add cleanup after use.

A nuance with spread: overrides with a field set to undefined will overwrite the default with undefined. For nested objects spread is shallow, so nested overrides must be merged separately.

In an interview: For data I most often make factory functions with defaults and a Partial override, which is short and type-safe. Factories that create entities through the API I wrap in fixtures, so cleanup is guaranteed.

Factory vs Builder and a Page Objects factory

How they differ:

CriterionFactoryBuilder
Callone functiona chain of methods
Objectsimple or mediumcomplex, many variations
Domain languagethrough parametersthrough methods like failed()
Amount of codeminimalmore boilerplate

In practice they are combined: a Builder may use a defaults factory inside.

Another use is a Page Objects factory, so as not to write new in every test. For example an App class with the getters loginPage, paymentsPage. But in Playwright this role is usually better played by fixtures: they are lazy and already integrated with the runner. A separate page factory makes sense when pages must be created dynamically inside a test (for example, after opening a new tab you got a new page).

When a factory is NOT needed: if the object is used in one test and its fields are the essence of the check. Then an explicit literal in the test reads better (DAMP).

A typical mistake: a factory that returns the same constant object, and a test mutates it. The next test gets spoiled data. A factory must always return a new object.

In an interview: A factory is one function for simple variations, and a Builder is a fluent API for complex objects with a domain language. A Page Objects factory in Playwright I usually replace with fixtures.