Three things to know before reading:
- A 2024 peer-reviewed study in the International Journal of Mobile Computing and Application found that mobile apps with crash rates above 1% see a 26% decrease in 30-day user retention. Race conditions from concurrent operations are a common crash source.
- The Luciq 2025 Mobile App Stability Outlook reports the median crash-free session rate at 99.95%. Reaching that bar requires catching concurrency bugs that only appear when multiple operations run at the same time.
- Standard functional testing runs one flow at a time. It misses the bugs that appear when a user taps a button twice before the first request completes, or when a push notification arrives mid-checkout.
Concurrency testing evaluates how an application behaves when multiple operations happen at the same time. On the backend, that means simulating hundreds or thousands of users hitting the same API endpoint simultaneously. On the client side, that means testing what happens when a mobile app processes overlapping user actions, background syncs, and system events.
Most concurrency testing guides focus on server-side load testing with tools like Apache JMeter. That matters. But for mobile apps, the client-side concurrency bugs are often harder to find and harder to reproduce. They're the crashes, data corruption, and UI glitches that happen only under specific timing conditions on a real device.
If your team runs end-to-end testing tools but only tests one user flow at a time in sequence, you're missing an entire category of bugs.
What concurrency testing catches that functional testing misses
Standard functional testing validates that a flow works: tap a button, fill a form, submit, check the result. It runs one action at a time, in order. If the login flow works and the checkout flow works, functional testing says the app is fine.
Concurrency testing breaks that assumption. It asks: what happens when two things happen at the same time?
The bugs it catches fall into specific categories:
- Race conditions: Two operations try to update the same data at the same time, and the result depends on which one finishes first. The outcome is unpredictable.
- Deadlocks: Two threads each wait for the other to release a resource. Neither can proceed. The app freezes.
- Data corruption: A background sync writes to a local database while the user is editing the same record on screen. One write overwrites the other.
- Resource contention: Multiple operations compete for memory, CPU, or network bandwidth. The app slows down or crashes under combined load.
Functional testing doesn't catch these because it doesn't create the timing overlap. A test that taps "Pay Now" once and waits for the result will never find the bug that happens when the user taps "Pay Now" twice before the first request completes.
Five concurrency bugs specific to mobile apps
Mobile apps have concurrency problems that web apps and backend services don't. The device itself introduces timing variables that server-side testing can't simulate.
1. Double-tap race conditions
A user taps a button. Nothing visible happens because the loading state is missing or too slow. The user taps again. The app fires two identical API requests. Both responses try to update the same UI state simultaneously. The app crashes or shows corrupted data.
This is the most common mobile race condition. It's caused by the gap between a user action and the UI feedback confirming that action was received.
2. Background sync conflicts
Most mobile apps sync data in the background (offline-first databases, push-triggered refreshes, periodic data pulls). If the user is editing a record on screen while a background sync updates the same record from the server, the app has a write conflict. Without proper conflict resolution, one write silently overwrites the other.
3. Push notification interrupts
A push notification arrives while the user is mid-flow (filling a form, completing a payment). The notification handler triggers a state change or navigation event that conflicts with the active flow. On some devices, this causes the app to reload or lose unsaved state.
This is especially common when push notifications trigger deep links that navigate the user to a different screen. If the payment request was in flight, the result has no handler waiting for it.
4. Network transition during API calls
The user starts an API call on Wi-Fi. The device transitions to cellular mid-request. The request times out on the old connection. The app retries on the new connection, but the first request eventually completes too. Now the server has processed the same action twice.
This affects any app where users are physically moving (ride-sharing, delivery, fitness) and connectivity changes frequently.
5. App backgrounding and foregrounding
The user opens the app, starts a flow, switches to another app, then comes back. On Android, the OS may have killed the activity in the background to reclaim memory. The app restores from a saved state, but the in-flight API request from before the backgrounding has either timed out or completed to a callback that no longer exists.
Testing this requires real-device execution because emulators handle memory pressure differently than physical hardware.
Backend vs. client-side: two layers of concurrency testing
For mobile apps, concurrency testing splits into two separate problems, each requiring different tools.
Backend concurrency (API and server)
This is the load testing side. You simulate hundreds or thousands of virtual users hitting the mobile backend simultaneously and measure response times, error rates, and throughput.
Tools for backend concurrency testing:
- Apache JMeter: open-source, mature, plugin-rich. Supports HTTP, REST, WebSocket, and database protocols. The standard choice for teams that need JMeter load testing for mobile APIs.
- k6: developer-friendly, JavaScript-based scripting, built for CI pipelines.
- Gatling: Scala/Java-based, strong for high-throughput scenarios.
Backend concurrency testing tells you whether the server can handle the load. It doesn't tell you whether the mobile client handles the server's responses correctly under concurrent conditions.
Client-side concurrency (device and app)
This is the harder problem. Client-side concurrency bugs involve the interaction between the app's UI, the device OS, background processes, and network state. No load testing tool simulates these. They require running the app on a real device and triggering overlapping events.
Testing client-side concurrency means:
- Running flows where the user interacts with the app while background operations are in progress
- Simulating interrupts (push notifications, incoming calls, permission dialogs) during active flows
- Testing network transitions (Wi-Fi to cellular, airplane mode toggle) during API calls
- Validating behavior after backgrounding and foregrounding the app during a multi-step flow
Teams that run automated regression testing on mobile often focus on happy-path flows. Adding concurrency scenarios to the regression suite is where the bugs that reach production get caught.
How Drizz tests client-side concurrency on real devices
Drizz runs tests on real Android and iOS devices, not emulators. That matters for concurrency testing because emulators don't reproduce real memory pressure, real network transitions, or real OS-level interrupts.
A Drizz test for a double-tap race condition scenario:
Tap on "Pay Now"
Tap on "Pay Now"
Validate "Payment Confirmed" is visible
Validate "Duplicate payment" is not visible
β
A Drizz test for push notification interruption during checkout:
Tap on "Proceed to Pay"
Type "4111111111111111" in "Card Number"
Wait for notification
Tap on notification banner
Navigate back
Validate "Card Number" still contains "4111111111111111"
Validate "Proceed to Pay" is still active
β
These are plain English commands. Drizz's Vision AI reads the screen visually, so it doesn't matter how the UI renders the payment form or the notification banner. If the element looks the same to a human, Drizz finds it.
For the app backgrounding scenario, Drizz supports background/foreground transitions and cold starts within test flows. The test can push the app to the background, wait, bring it back, and validate that the state is intact, all on a real device where the OS might actually reclaim memory.
Drizz also handles the popups and system dialogs that interrupt concurrent flows. A built-in popup agent (called the unblocker) automatically dismisses permission prompts, cookie banners, and system alerts without extra test code. Those interrupts are often the trigger for concurrency bugs, and if the test framework can't handle them, the test itself becomes the point of failure.
For teams building mobile e-commerce apps where payment flows and inventory updates happen concurrently, Drizz provides a way to test the client-side behavior that JMeter and other backend tools can't reach.
FAQ
What is concurrency testing?
Testing how an application behaves when multiple operations or users act on it at the same time.
How is concurrency testing different from load testing?
Load testing measures server capacity under traffic. Concurrency testing checks for race conditions, deadlocks, and data corruption.
Can JMeter test mobile app concurrency?
JMeter tests the backend APIs. It can't test client-side behavior like double-tap crashes or push notification conflicts on the device.
What causes race conditions in mobile apps?
Two operations updating the same data or UI state simultaneously, with the outcome depending on unpredictable execution order.
Do emulators work for mobile concurrency testing?
Partially. Emulators miss real memory pressure, real network transitions, and real OS-level interrupts that trigger concurrency bugs.
How do you test push notification interrupts?
Trigger a notification during an active user flow and validate that the app preserves state and handles the interrupt correctly.
β


