How would you make project tests safe to run in parallel?
Instruction: Define data ownership, show a concrete create/change/read/cleanup example and address interrupted cleanup.
Eight test workers currently rename and delete the same shared project. The fictional isolated test environment provides POST /test-support/projects accepting a unique namespace and name, returning 201 with string ID. PATCH and GET on /projects/{id} return 200; GET includes ID, name and namespace. DELETE /test-support/namespaces/{namespace} is idempotent, deletes only that namespace and returns 204 even when empty. These administrative endpoints exist only in the controlled test environment and the runner is authorized to use them. Shared authenticated browser sessions do not isolate server records.
Updated
Example Answer
I would give every test attempt a unique namespace and create the records it owns. Each test would mutate and read only its own project. A fresh browser context isolates browser state, but it does not stop two sessions from editing the same database row.
Cleanup would target the exact namespace in a finally block, never all test-looking projects. Because creation can succeed before its response is lost, cleanup by the known namespace is safer than depending only on a returned ID. I would also attach an expiry to test namespaces and run an authorized, bounded janitor for abandoned records after terminated workers. A cleanup failure must be visible; the runner should retain both the primary failure and cleanup diagnostics when both occur.
Worked exhibit
import { randomUUID } from 'node:crypto';
import { test, expect } from '@playwright/test';
test('renames its own project', async ({ request }) => {
const namespace = `qa-${randomUUID()}`;
const failures: unknown[] = [];
try {
const created = await request.post('/test-support/projects', {
data: { namespace, name: 'Before' },
});
expect(created.status()).toBe(201);
const { id } = await created.json();
expect(id).toEqual(expect.any(String));
const changed = await request.patch(`/projects/${encodeURIComponent(id)}`, {
data: { name: 'After' },
});
expect(changed.status()).toBe(200);
const readback = await request.get(`/projects/${encodeURIComponent(id)}`);
expect(readback.status()).toBe(200);
expect(await readback.json()).toMatchObject({ id, name: 'After', namespace });
} catch (error) {
failures.push(error);
} finally {
try {
const cleanup = await request.delete(
`/test-support/namespaces/${encodeURIComponent(namespace)}`
);
expect(cleanup.status()).toBe(204);
} catch (error) {
failures.push(error);
}
}
if (failures.length === 1) throw failures[0];
if (failures.length > 1) {
throw new AggregateError(failures, 'Test and cleanup both failed');
}
});
The snippet aggregates errors only to preserve a primary failure when cleanup also fails; it rethrows every recorded failure. It uses ES2021 AggregateError. A shared fixture can implement the same lifecycle once for many tests and preserve separate teardown diagnostics. The namespace cleanup contract must include descendants; otherwise each child record needs tracked cleanup. A retry gets a new namespace, while the previous namespace remains eligible for expiry cleanup.
What the interviewer is assessing
Core checks (1 point each):
- Creates a per-attempt namespace and reads its own changed record.
- Cleans only owned data, including after a lost create response.
- Preserves the original test failure and any cleanup failure.
Stronger (+1 after all core checks): Explains why known-namespace cleanup can recover after a lost create response and proposes a bounded janitor for workers that cannot run teardown.
Acceptable alternatives: A fixture lifecycle can replace try/finally if ownership and diagnostics are equivalent.
Red flags: Uses one shared mutable account/project, serializes the whole suite as the permanent solution, deletes broad prefixes across other runs or assumes finally executes after process termination.
Tempting answer that misses the point
Weak answer: Fresh browser contexts isolate the shared project.
Why it fails: Browser isolation does not isolate a shared backend record.
Follow-up
Can read-only reference data be shared?
Example response: Yes, if it is immutable for the test run and the tests cannot mutate it through application behavior. I would distinguish shared catalog fixtures from test-owned carts, projects and users. If reference data must change, I would version or clone it so one test cannot invalidate another test’s assumptions.
Related Questions
-
easy
-
easy
-
easy
-
easy
-
easy
-
easy