Sparround

Common JS patterns in test code

Automation projects keep hitting the same handful of problems. They have established solutions — and being able to turn these patterns into code is the main thing separating middle from junior in an interview:

  • A retry wrapper — retrying an unstable infrastructure operation
  • Polling / waitFor — waiting for an async result the UI never shows
  • A data builder (factory) — building fresh, unique test data for every test
  • Utility composition — combining small, clean functions

One principle underpins all of them: tests must not depend on each other. Shared mutable state, ordering assumptions and "data created by the previous test" are the biggest source of flakiness.

Retry and polling are not the same thing — knowing the difference lands well in an interview.

Retry means executing the operation again. The failed attempt was a real action (a POST request, creating a resource) and you repeat it. The wait grows with each attempt — exponential backoff (300ms → 600ms → 1200ms) — plus a small random addition (jitter) so parallel workers do not retry in lockstep and hammer the server.

Polling means re-checking a state. Nothing is re-executed; you simply ask "is it ready yet?" at a steady interval until the condition holds or the timeout expires. A queued message being processed, an email arriving, an invoice being issued — all polling problems.

The same requirement applies to both: a timeout is mandatory, and the error message must say what you were waiting for. Timed out after 30000ms is useless; Timed out after 30000ms waiting for invoice of order 1042 (last status: pending) shows the problem at a glance.

PatternProblem it solvesIts risk
Retry wrapper (with backoff)unstable infrastructure: network, 502s, a test-data serviceit can hide a real product bug
Polling / waitForan async result invisible in the UI (queue, email, invoice)a long timeout slows down the whole suite
Data builder / factoryfresh, unique, isolated data for every testthe database fills with junk if cleanup is forgotten
Utility compositionbuilding complex scenarios from small functionsover-abstraction hurts readability
Fixture (at framework level)setup and guaranteed teardownhidden dependencies make the test hard to read

The most dangerous use of retry is retrying assertions. Retrying an operation is legitimate; "the assertion failed, let's check once more" statistically hides real regressions. The rule: retry applies only to infrastructure operations (network, resource creation, an external service), never to product behaviour.

The same logic applies to the framework-level retries setting: it helps in CI, but if the retry count keeps creeping up, that is a sign the suite is sick. The healthy approach is to measure retries (how many tests pass only on the second attempt) and investigate those tests.

When asked about retries in an interview, add this sentence: "A retry masks a symptom, so every retry must be recorded and its cause investigated."

📚 Sources and documentation