English for Cypress and Playwright Testing
Master the vocabulary for discussing selectors, flakiness, fixtures, and assertions when writing end-to-end tests with Cypress and Playwright.
End-to-end testing conversations tend to circle around one recurring problem: flakiness. Whether you’re using Cypress or Playwright, the vocabulary for describing why a test fails intermittently — race condition, auto-waiting, network stubbing — is what actually helps a team fix the root cause instead of just adding another retry and hoping.
Key Vocabulary
End-to-end test (E2E test) A test that exercises an application the way a real user would, driving an actual browser through a full flow rather than testing an isolated unit or function. Example: “This bug wouldn’t have been caught by our unit tests alone — it only shows up when the full checkout flow runs end-to-end against a real browser.”
Selector
The string or strategy used to locate a specific element on the page, such as a CSS selector, text content, or a dedicated test attribute like data-testid.
Example: “This selector is matching on a CSS class used purely for styling, so it breaks every time a designer changes that class — we should switch to a dedicated test attribute instead.”
Auto-waiting The built-in behavior of both Cypress and Playwright to automatically retry an assertion or action for a period of time before failing, rather than requiring explicit manual waits. Example: “We don’t need an explicit wait here — auto-waiting already retries this assertion until the element appears or a timeout is reached.”
Flaky test (flakiness) A test that passes and fails inconsistently across identical runs without any change to the underlying code, usually caused by timing issues, unhandled asynchronous behavior, or test interdependence. Example: “This test is flaky specifically under CI load, which usually points to a race condition that just doesn’t reproduce reliably on a fast local machine.”
Fixture Predefined, reusable test data — like a JSON file representing an API response — used to set up consistent, predictable conditions for a test rather than relying on live or random data. Example: “We’re loading this fixture instead of hitting the real API, so the test’s input data stays consistent no matter when or where it runs.”
Network stubbing / interception The practice of intercepting network requests during a test and returning a controlled, predefined response, rather than letting the request hit a real backend. Example: “We stubbed the payment API response here so this test doesn’t depend on a real payment gateway being available and doesn’t risk making an actual charge.”
Assertion A statement in a test that verifies an expected condition is true, such as an element being visible or containing specific text, causing the test to fail if the condition isn’t met. Example: “This assertion is checking that the element exists, but not that it’s actually visible — a hidden element could still pass this check misleadingly.”
Test isolation The principle that each test should run independently, without depending on the state left behind by a previous test, so that tests can run in any order or in parallel reliably. Example: “This test only passes when it runs after another specific test, which means we’re missing test isolation — each test should set up its own state from scratch.”
Common Phrases
In code reviews:
- “This selector is tied to a CSS class used for styling — could we add a dedicated
data-testidso the test doesn’t break when the design changes?” - “We’re adding a hard-coded wait here instead of relying on auto-waiting — that usually masks a real timing issue rather than fixing it.”
- “This test depends on data left behind by a previous test, which breaks test isolation and will fail if we ever run tests in a different order.”
In standups:
- “Yesterday I replaced a chain of brittle CSS selectors with
data-testidattributes; today I’m confirming that fixed the flakiness we saw in CI.” - “I’m blocked on a flaky test that fails intermittently only in CI — I suspect a race condition between an API call and a UI update that isn’t accounted for.”
- “I finished adding network stubbing for the payment flow test, so it no longer depends on a real payment gateway being reachable.”
In test strategy discussions:
- “We should rely on auto-waiting and proper assertions instead of arbitrary sleep calls, since sleeps either waste time or aren’t long enough under load.”
- “Fixtures make our tests reproducible regardless of what’s actually in the staging database at the time they run.”
- “Test isolation is worth the setup cost — flaky, order-dependent tests erode trust in the whole suite much faster than a slightly slower but reliable one.”
Phrases to Avoid
Saying “the test is broken” for a flaky test. Say instead: “this test is flaky — it fails intermittently without a code change, which usually points to a race condition or timing issue” — a broken test fails consistently, while a flaky test needs a different diagnostic approach entirely.
Saying “just add a wait” as the default fix for flakiness. Say instead: “let’s check whether auto-waiting is already covering this, or whether the real issue is an assertion checking the wrong condition” — arbitrary waits often just paper over a race condition instead of resolving it.
Saying “the selector doesn’t work” without specifying why. Say instead: “this selector is tied to a styling class that changes often — let’s use a dedicated test attribute instead” — this identifies the actual instability, rather than treating the failure as random.
Quick Reference
| Term | How to use it |
|---|---|
| selector | ”Use a data-testid selector instead of a styling class.” |
| auto-waiting | ”Auto-waiting retries this assertion before timing out — no manual wait needed.” |
| flaky test | ”This test is flaky under CI load, likely a race condition.” |
| fixture | ”We load a fixture instead of depending on live API data.” |
| network stubbing | ”We stubbed the payment API so no real charge happens during tests.” |
| test isolation | ”Each test should set up its own state, not depend on a prior test.” |
Key Takeaways
- Distinguish a flaky test from a broken one — flaky failures need root-cause diagnosis (timing, race conditions), not just a rerun.
- Prefer dedicated test attributes over styling-based selectors, since the latter breaks whenever a designer changes a class name.
- Trust auto-waiting before reaching for arbitrary manual waits, which often mask rather than fix a real timing issue.
- Use fixtures and network stubbing to keep tests reproducible and independent of live backend state.
- Treat test isolation as a non-negotiable design principle — tests that depend on run order erode confidence in the whole suite.
Navigating Nuance: English for Testing Collaboration
Let’s be frank – testing isn’t just about running code; it’s a fundamentally collaborative process. When you’re working with colleagues, especially those whose first language isn’t English, precision in communication is absolutely critical. It’s not enough to simply state what the test does; you need to articulate why it does it, and how its behavior aligns with expectations. This often involves a subtle shift in vocabulary and phrasing that can significantly impact feedback quality and overall team efficiency.
Consider this scenario: Sarah, a developer new to the project, submits a PR containing a Cypress test designed to verify user input on a registration form. The reviewer, Mark, leaves a comment: “This selector is fragile – it relies heavily on specific text content. Consider using more robust locators like data attributes or CSS properties.” While technically accurate, this comment could be interpreted as criticism. Sarah might feel defensive, unsure of why the selector is fragile and how she can improve it. A more constructive approach would be: “I understand your concern about the selector’s fragility. I’ve been using data-id attributes for input fields to provide a more stable target. Could you walk me through some alternative locator strategies that might also be resilient to changes in the UI?” This shifts the focus from simply identifying a problem to collaboratively finding a solution.
Similarly, when describing flakiness – a common frustration with end-to-end tests – avoid overly technical jargon. Instead of saying “the test is flaky due to network latency,” try: “This test occasionally fails intermittently, likely due to variations in the server response times. We should investigate if we can introduce timeouts or retry mechanisms.” Phrases like “intermittently” and “variations in response times” are far more accessible and promote a shared understanding of the issue. When writing PR descriptions, be explicit about the intent behind the test: “This test verifies that new user registration successfully creates an account and sends a welcome email.” The goal is to paint a clear picture for anyone reading it – not just another developer proficient in technical English.
Finally, remember that assertions themselves benefit from careful wording. Instead of saying “the element exists,” which is technically true but lacks context, try: “The registration form’s submit button is visible and enabled when the required fields are populated.” This communicates what was verified – not just that it existed.
// Example Cypress code demonstrating a robust selector strategy
it('should register a new user', () => {
cy.visit('/register');
cy.get('[data-id="username"]').type("testuser");
cy.get('[data-id="email"]').type("test@example.com");
cy.get('#submitButton').click();
cy.contains('Welcome, testuser!').should('be.visible');
});
This example demonstrates using a data-id attribute as a more stable selector than relying solely on text content within the input field itself. This is a practical application of the vocabulary discussed above – demonstrating a proactive approach to creating resilient and understandable tests.