A black and white image of a laptop showing code on the screen

Seven practical ways to stop E2E tests from breaking

Profile picture for user matthew
Matthew Clark

End-to-end (E2E) testing ensures your application works seamlessly from the user’s perspective. It’s a critical part of any modern testing strategy. But if you’ve worked with E2E tests before, you know how fragile they can be. E2E tests often break due to small code changes, UI updates, or environment inconsistencies. 

The good news? Strong code, smart choices, and proactive maintenance can stop broken E2E tests from taking over your week. Below, I’ve shared the tactics our team uses to stay two steps ahead — delivering flawless software, without the constant firefighting.

1. Prioritize test isolation and independence

E2E tests often break when they depend on shared states or external systems. Here’s how to mitigate this:

  • Isolate test data — Use dedicated test data sets or mock APIs to avoid conflicts. Tools like test data generators or in-memory databases can ensure clean, reproducible data for each test run, avoiding potential issues of corrupted data.
  • Avoid test dependencies — Write tests that don’t rely on the execution order of other tests. Each test should set up and tear down its own environment to prevent cascading failures.
  • Consider using mocking for external services — Generally, E2E tests should ensure external services like payment gateways or APIs are working. But there are edge cases where you might want to test with a mocked response without relying on the external service to trigger specific conditions, such as timeouts, error responses, or unusual data payloads.

2. Use robust selectors

UI changes like CSS or DOM updates often break E2E tests. You can minimize the chances of this happening by creating resilient selectors:

  • Leverage accessibility features — Tools like Selenium or Playwright can use ARIA labels and roles, which are more stable than visual styling.
  • Use data attributes — Instead of relying on fragile selectors like CSS classes or IDs, use custom data-test-id attributes that are less likely to change during development.
  • Regularly review selectors — Work closely with developers to ensure UI changes are communicated and selectors are updated proactively.