We use analytics to understand how our website is used. No personal data is collected.

October 6, 2025 · Piyush Ranjan Mishra

Automated E2E Testing for React Native: Jest, Detox, and CircleCI

TestingReact NativeCI/CDR Systems

At R Systems I implemented unit and automated E2E testing for React Native apps using Jest, Enzyme, CircleCI, and Detox, as part of leading client projects including dotstudioPro. It cut inbound bug reports by around 10% and lifted app store ratings 8–10% across the partner apps. Mobile E2E testing has a well-earned reputation for being flaky and slow enough that teams eventually start ignoring red builds — the actual work here was building a suite that stayed trustworthy instead of becoming background noise.

Unit tests and E2E tests were solving different problems, on purpose

Jest and Enzyme covered component-level unit tests — rendering logic, prop handling, state transitions in isolation, fast enough to run on every save during development. Detox covered true end-to-end flows — launching the actual app on a simulator/emulator, tapping through a real user flow, asserting on real rendered screens. Conflating these (writing slow, flaky E2E-style tests for logic that a fast unit test could verify in milliseconds) is a common mistake that makes the whole suite slower and less pleasant to work with for no real coverage benefit. The rule I used: if a unit test can verify it in isolation, it’s a unit test — E2E tests were reserved specifically for flows that only make sense end-to-end (navigation across screens, real API interaction, platform-specific behavior a unit test can’t observe).

What made Detox tests stop being flaky

Detox’s core value proposition is synchronization with the app’s own async activity — it waits for network requests, animations, and the JS thread to settle before proceeding, instead of relying on fixed sleeps like older mobile E2E tools. Getting this synchronization to actually work reliably meant being disciplined about not fighting it — no manual sleep() calls papering over an element that wasn’t ready, because that’s exactly the anti-pattern Detox’s synchronization is meant to replace, and mixing the two approaches produces the worst of both. Every flaky test I debugged eventually traced back to either a manual wait masking a real synchronization gap Detox didn’t know about (usually a custom native module or an animation Detox couldn’t observe), or a test asserting on state before an async operation had genuinely completed.

CircleCI: making E2E tests part of the actual merge gate, not a manual step

The pipeline ran unit tests on every push (fast, cheap, immediate feedback) and the full Detox E2E suite on PRs targeting main (slower, ran against a real built app on a simulator, gated the merge). The critical design decision was making E2E failures actually block merges, not just report status that got ignored — a test suite that reports red without consequence trains a team to ignore red, and that erosion happens faster than most teams expect. This required investing in making the E2E suite reliable enough that a red build was trustworthy signal, not noise — an unreliable gate that blocks merges gets disabled by an annoyed team within weeks; a reliable one earns the right to actually block.

Building white-label testing into the same pipeline

Because this client work included white-label website and app generation tooling for dotstudioPro’s partner network, the E2E suite needed to verify core flows across differently-themed builds, not just one canonical app. Rather than a fully separate test suite per white-label variant (an unmaintainable multiplication of test code as partners were added), core E2E flows were written against stable selectors and structural assertions that held regardless of theme, with a smaller set of variant-specific visual/branding checks layered on top. This kept the bulk of test coverage shared and maintainable while still catching partner-specific regressions.

The maintenance cost nobody budgets for upfront

E2E test suites need ongoing maintenance as the app’s UI evolves — a selector that broke because a component was refactored is expected, not a sign something’s wrong with the approach. What separated a maintainable suite from a neglected one was treating test maintenance as a normal part of the development workflow (fix the test in the same PR that changes the UI it covers) rather than a separate cleanup task that piles up and eventually gets a “just skip these for now” treatment that never gets revisited.

What I’d tell a team setting this up today

Keep unit and E2E tests solving genuinely different problems — don’t let E2E tests creep into covering things a unit test would verify faster and more reliably. Invest in Detox’s synchronization working correctly rather than working around it with manual waits. And treat a red E2E build as something that must block merges from day one — a test suite that’s allowed to fail without consequence stops being a test suite and becomes documentation nobody reads.