How to Test Chat Functionality on Android (Complete Guide)

Chat is often the most interactive part of an Android application. Users expect messages to appear instantly, notifications to be reliable, and the UI to stay responsive even when the conversation gro

March 14, 2026 · 18 min read · How-To Guides

Why Chat Functionality Demands Rigorous Testing

Chat is often the most interactive part of an Android application. Users expect messages to appear instantly, notifications to be reliable, and the UI to stay responsive even when the conversation grows long. When any of these expectations fail, users abandon the app, leave negative reviews, or migrate to competitors. In production, chat bugs manifest as missed messages, duplicate notifications, UI freezes, or even security leaks such as unintended exposure of message content. Because chat touches networking, threading, UI rendering, data persistence, and accessibility layers, a defect in any one of these areas can cascade into a broader failure. Testing chat therefore is not a nicety; it is a prerequisite for retaining trust and ensuring the core value proposition of the app works under real‑world conditions.

Core Challenges in Chat Testing on Android

Testing chat on Android introduces several technical hurdles that are less prevalent in other feature sets.

Asynchronous Message Flow

Messages arrive from a server over WebSocket, FCM, or long‑polling HTTP. The arrival time is non‑deterministic, which makes it hard to assert UI state at a fixed point in a test. Flaky tests often stem from assuming a message will be processed within a hard‑coded timeout.

Concurrency and Threading

Chat UI typically runs on the main thread while background workers handle encryption, media upload/download, and database writes. Race conditions can cause the UI to show stale data, or a background task to block the main thread, leading to ANRs.

State Persistence

Chat apps store conversation history in SQLite, Room, or Protobuf files. Tests must verify that messages survive process kills, device rotations, and low‑memory scenarios. Improper cleanup can leave orphaned rows that bloat the database and degrade performance overrun‑time.

Multimedia Handling

Images, videos, voice notes, and file attachments introduce MIME type checks, transcoding, and thumbnail generation. Each format brings its own set of failure modes: corrupted files, unsupported codecs, or storage‑permission denials.

Accessibility Overlays

TalkBack, Switch Access, and font‑size scaling change the layout dynamics. A chat bubble that looks fine at default size may overflow or become unreadable when the user enlarges text. Ensuring that accessibility services can navigate the list, announce new messages, and allow message composition without visual cues is essential.

Security and Privacy Surface

End‑to‑end encryption, local database encryption, and secure key storage are common in chat apps. A test must confirm that keys are never written to logs, that screenshots are blocked when appropriate, and that message content is not inadvertently exposed via clipboard or notification previews.

Test Matrix for Chat Features

Below is a comprehensive matrix that groups test ideas by category. Each row describes a scenario, the expected outcome, and the primary verification point. Use this matrix as a checklist when building manual or automated test suites.

CategoryIDScenarioExpected OutcomeVerification Point
Happy PathHP1Send a text message from user A to user BMessage appears in both users’ chat views with correct timestamp and sender labelUI bubble text matches input; timestamp within 2 s of send
Happy PathHP2Receive a push notification while app is in backgroundNotification shows sender name and message preview; tapping opens chat at the new messageNotification content matches payload; launching intent lands on correct conversation
Happy PathHP3Send an image attachmentImage uploads successfully, thumbnail appears in chat, full‑size image viewable on tapUpload returns HTTP 200; thumbnail displayed; full image loads without corruption
Happy PathHP4Scroll through 200 previous messagesList scrolls smoothly; no duplicate or missing items; memory usage stays under 80 MBEspresso idling resource reports no jank; Android Studio profiler shows steady memory
Error PathEP1Send message when device has no networkMessage is queued locally, a retry icon appears, and send resumes when connectivity returnsLocal DB stores unsent flag; retry attempts after network change; message sent after reconnect
Error PathEP2Server returns 500 on message sendApp shows an error toast, keeps message in draft state, and allows user to resendToast text matches error handling; message remains in input field after failure
Error PathEP3Receive a malformed JSON payloadApp discards the payload, logs an error, and does not crash or UI freezeLogcat contains expected error; chat view unchanged; no ANR
Edge CaseEC1Send a message with the maximum allowed length (e.g., 10 000 characters)Message is transmitted, stored, and displayed correctly without truncationCharacter count matches input; DB column holds full text
Edge CaseEC2Rapidly send 50 messages in succession (< 200 ms between each)All messages appear in order; no UI lag or dropped messagesSequence numbers in DB match send order; UI shows each bubble
Edge CaseEC3Receive a message while composing a long text; the IME is openIncoming message appears above the composition field; the cursor stays in the input fieldComposition text unchanged; new bubble inserted above IME
Edge CaseEC4Rotate device while a media upload is in progressUpload continues; UI shows persistent progress indicator; after rotation, progress bar reflects correct stateUpload service not bound to Activity; progress retained via ViewModel
AccessibilityAC1TalkBack enabled; navigate chat list with swipe gesturesEach bubble is announced with sender, timestamp, and message content; new messages trigger announcementTalkBack output matches expected strings; focus moves to new bubble
AccessibilityAC2Increase font size to 200 %Chat bubbles expand, text wraps, no overlapping UI elements; input field remains usableLayout inspector shows no clipping; user can still type and send
AccessibilityAC3High contrast mode enabledAll UI elements meet WCAG AA contrast ratio (≥ 4.5:1 for text)Contrast checker reports pass for bubble background/text
Security/PrivacySP1End‑to‑end encryption enabled; verify that plaintext never appears in logs or LogcatNo message content is logged at any level (verbose, debug, info)grep of Logcat for message substring returns empty
Security/PrivacySP2Screenshot blocked in secure chat (if policy set)Taking a screenshot yields a blank image or system‑blocked notificationadb shell screencap yields all‑black pixels or system toast
Security/PrivacySP3Clipboard protection: copying a message does not leave plaintext in clipboard after 5 sClipboard content is cleared or replaced with a placeholder after timeoutadb shell service call clipboard get returns empty or placeholder
Security/PrivacySP4Local database encrypted with SQLCipher; attempting to read raw file yields gibberishRaw .db file opened with SQLite command shows no readable texthexdump of file shows no plaintext strings; decrypted via correct key yields expected schema

Manual Testing Approach: Step‑by‑Step

Even when automation is in place, a disciplined manual exploratory pass catches issues that scripts overlook, especially those tied to timing, device state, or human perception.

Environment Setup

  1. Device selection – Use at least one physical device running Android 11 (API 30) and one running Android 13 (API 33) to capture OS‑specific behavior.
  2. Developer options – Enable USB debugging, disable battery optimizations for the app under test, and turn on “Show CPU usage” to spot spikes.
  3. Network simulation – Use the built‑in network speed emulator (via adb shell cmd network-manager profile set ) or a tool like Clumsy to inject latency, packet loss, or bandwidth limits.
  4. Logging – Start adb logcat -v threadtime > chat_test.log to capture timestamps and stack traces.
  5. Test accounts – Create two or more test users with known credentials; avoid using real personal data.

Baseline Exploration

Before writing any test steps, spend five minutes freely navigating the chat screen:

Take note of any unexpected UI jumps, delayed appearance of messages, or missing notifications. Record the exact steps and the observed behavior in a spreadsheet; this becomes the seed for your manual test cases.

Scripted Manual Tests

Translate the baseline observations into repeatable scripts. Each script should be short enough to execute in under two minutes, yet detailed enough to be handed to another tester.

Example Script – Send Image While Offline

  1. Disable Wi‑Fi and mobile data (adb shell svc wifi disable && adb shell svc data disable).
  2. Open chat with user B.
  3. Tap the attachment icon, choose a JPEG ≈ 2 MB from device storage.
  4. Observe that a “sending…” overlay appears and a retry icon shows after a few seconds.
  5. Re‑enable Wi‑Fi (adb shell svc wifi enable).
  6. Verify that the upload resumes, the retry icon disappears, and the image appears in both users’ views.
  7. Check Logcat for any NetworkOnMainThreadException or upload‑service errors.

Repeat the script with different file types (PNG, GIF, PDF) and varying sizes to uncover limits in the upload queue.

Logging and Observation

During each manual run, capture:

If a test fails, the collected artifacts let you reproduce the failure locally and provide developers with concrete evidence.

Automated Testing on Android

Automation provides repeatability and regression safety. For chat, the automation strategy must address asynchrony, threading, and external dependencies.

Unit and Integration Tests for Chat Logic

Isolate the view‑model or use‑case layer that handles sending/receiving messages. Use JUnit 5 with Mockito or MockK to mock the networking repository and the database DAO.


@OptIn(ExperimentalCoroutinesApi::class)
class ChatViewModelTest {

    private lateinit var viewModel: ChatViewModel
    private val repository = mock<ChatRepository>()
    private val dispatcher = UnconfinedTestDispatcher()

    @BeforeEach
    fun setUp() {
        Dispatchers.setMain(dispatcher)
        viewModel = ChatViewModel(repository)
    }

    @AfterEach
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `sendMessage updates UI and calls repository`() {
        // given
        val input = "Hello world"
        whenever(repository.sendMessage(any())).thenReturn(Completable.complete())

        // when
        viewModel.onSendClicked(input)
        dispatcher.dispatchUntilIdle()

        // then
        verify(repository).sendMessage(eq(input))
        assertTrue(viewModel.messages.value?.last()?.text == input)
    }
}

Running these tests on every commit guarantees that the core logic remains sound even when the UI layer changes.

UI Automation with Espresso

Espresso excels at synchronizing with the UI thread. For chat, the main difficulty is waiting for background messages to appear. Use IdlingResource implementations tied to your networking layer or to a LiveData observable.


class ChatEspressoTest {

    @get:Rule
    val activityRule = ActivityTestRule(ChatActivity::class.java)

    private val networkIdling = object : IdlingResource {
        private var callback: IdlingResource.ResourceCallback? = null
        override fun getName() = "NetworkIdle"
        override fun isIdleNow(): Boolean = ChatRepository.isIdle()
        override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
            this.callback = callback
        }
    }

    @BeforeEach
    fun registerIdling() {
        IdlingRegistry.getInstance().register(networkIdling)
    }

    @AfterEach
    fun unregisterIdling() {
        IdlingRegistry.getInstance().unregister(networkIdling)
    }

    @Test
    fun sendTextMessage_showsInList() {
        // type message
        onView(withId(R.id.input_message))
            .perform(typeText("Espresso test"), closeSoftKeyboard())

        // press send
        onView(withId(R.id.btn_send)).perform(click())

        // wait for the bubble to appear
        onView(withText("Espresso test"))
            .check(matches(isDisplayed()))
    }
}

Espresso tests run fast on emulators or physical devices and give confidence that the UI reacts correctly to user actions.

UI Automation with UiAutomator2

When you need to interact with system dialogs, permission prompts, or the notification shade, UiAutomator2 is the better choice. It operates outside the app’s process, allowing you to test scenarios like “notification tap opens chat”.


@RunWith(AndroidJUnit4.class)
public class ChatNotificationUiTest {

    @Before
    public void setUp() throws Exception {
        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        device.pressHome();
    }

    @Test
    public void notificationTapOpensChat() throws Exception {
        // Assume a background service has posted a notification with text "Test msg"
        UiObject2 notification = device.findObject(By.text("Test msg"));
        assertNotNull(notification);
        notification.click();

        // Verify chat activity is in foreground
        UiObject2 chatTitle = device.findObject(By.res("com.example.app:id/chat_title"));
        assertEquals("Friend", chatTitle.getText());
    }
}

UiAutomator2 also lets you simulate gestures like long‑press on a message bubble to open a context menu.

Network Mocking and Interception

To eliminate flakiness caused by real network latency, use OkHttp’s MockWebServer or WireMock to simulate server responses. This lets you test error paths (500, timeout) and edge cases (delayed push, out‑of‑order messages) deterministically.


@BeforeEach
fun startMockServer() {
    mockWebServer = MockWebServer()
    mockWebServer.start()
    // configure repository to point to mockWebServer.url("/")
}

@Test
fun sendMessage_whenServerReturns500_showsErrorToast() {
    mockWebServer.enqueue(MockResponse().setResponseCode(500))
    // trigger send
    onView(withId(R.id.btn_send)).perform(click())
    // verify toast
    onView(withText("Failed to send message"))
        .inRoot(isToast())
        .check(matches(isDisplayed()))
}

By varying the enqueued responses, you can cover the entire error matrix without relying on a flaky test environment.

Performance and Load Testing

Chat apps can suffer from jank when the message list grows large. Use Android Studio Profiler or Systrace to capture frame timings while automatically scrolling through a list of 500 messages.


# Start profiling
adb shell am profile start com.example.app /data/local/tmp/chat_trace.trace
# Run an Espresso script that scrolls
adb shell am instrument -w -r \
    -e debug false \
    -e class com.example.app.test.ChatPerfTest#testScrollLargeList \
    com.example.app.test/androidx.test.runner.AndroidJUnitRunner
# Stop profiling
adb shell am profile stop
# Convert trace to readable format
python -m systrace --from-file=/data/local/tmp/chat_trace.trace

Look for frames exceeding 16 ms (60 fps) and investigate layout passes triggered by notifyItemChanged or inefficient view holders.

Tooling Comparison Table

The following table summarizes the strengths and weaknesses of the main automation tools for chat testing on Android. It helps you decide where to invest effort based on your team’s skill set and the specific aspects of chat you need to validate.

ToolPrimary UseStrengths for ChatWeaknesses / GapsTypical Setup Effort
EspressoIn‑app UI tests (same process)Synchronizes with UI thread, fast execution, excellent for validating message send/receive UI, works well with IdlingResource for async waitsCannot interact with system dialogs, notifications, or other apps; limited to UI layerLow – add dependency, write IdlingResource if needed
UiAutomator2Cross‑app / system UI testsCan press home, open notification shade, handle runtime permissions, test deep links from notificationsSlower than Espresso, more brittle due to reliance on resource IDs that may change, harder to synchronize with app‑specific async eventsMedium – need to manage device state, handle synchronization manually
MockWebServer / WireMockNetwork layer simulationEnables deterministic testing of error codes, latency, throttling, and payload validation; isolates app from server flakinessRequires abstraction of networking layer; does not test actual server‑side logicLow – add library, configure base URL
Android Profiler / SystracePerformance & GPU analysisShows frame drops, overdraw, thread contention, memory allocation spikes; essential for scrolling performance validationRequires manual interpretation; not a pass/fail test frameworkMedium – set up profiling scripts, analyze traces
SUSA (Autonomous Explorer)Persona‑driven exploratory testingSimulates real user behaviors (curious, impatient, accessibility, adversarial) without scripts; discovers dead ends, unhandled edge cases, and accessibility violations that scripted tests missResults are probabilistic; needs baseline to measure improvement; best used as a complement to scripted suitesLow – CLI install, point at APK or URL, run with desired persona profiles
Firebase Test LabDevice farm executionRuns Espresso/UiAutomator2 scripts on a wide range of real devices and API levels; provides video, logs, and performance metricsCosts increase with test duration; limited to predefined test types; no built‑in persona modelingMedium – upload APK, select devices, trigger test matrix

When constructing a chat testing pipeline, a common pattern is:

  1. Unit tests for business logic (JUnit/Mockito).
  2. Espresso for core UI flows (send, receive, scroll).
  3. UiAutomator2 for notification and permission scenarios.
  4. MockWebServer to inject network errors and latency.
  5. SUSA runs nightly to surface regressions that scripts never consider (e.g., a particular combination of font‑size scaling and dark mode that clips a message bubble).
  6. Profiler captures performance trends over time.

Autonomous, Persona‑Driven Exploration with SUSA

While scripted tests verify known paths, they cannot anticipate every way a real user might interact with a chat screen. Autonomous testing tools like SUSA explore the app using modeled user personas, each with distinct behavior patterns, goals, and tolerance for friction. This approach surfaces bugs that hide in rarely exercised corners of the UI, especially in chat where the combination of gestures, timing, and system state creates a combinatorial explosion.

How SUSA Works

SUSA treats the installed APK as a black box. After launch, it builds a state graph of screens (activities/fragments) and interactive elements (buttons, text fields, RecyclerView items). It then drives the device using a selected persona profile, which defines:

During each run, SUSA records every action, the resulting state, and any observable failures (crashes, ANRs, unhandled exceptions, accessibility violations). It also remembers dead ends—screens from which no further progress is possible—and avoids revisiting them in subsequent runs, making each execution smarter than the last.

Persona Profiles Relevant to Chat

PersonaTypical BehaviorWhat It Uncovers in Chat
CuriousTaps every visible element, long‑presses on avatars, opens context menus, tries to swipe left/from the chat list to other tabs.Finds hidden actions (e.g., long‑press to reply, message reactions) that are not documented or that lack proper accessibility labels.
ImpatientSends messages rapidly, rotates device every 2‑3 seconds, cancels outgoing messages by swiping.Exposes race conditions in message ordering, duplicate sends, or UI freezing when the input field is rapidly cleared and refilled.
NoviceUses only the most obvious UI (send button, text field), avoids gestures, takes longer to locate the attachment icon.Highlights discoverability problems: missing affordances for attaching files, unclear error states, or lack of tooltips.
ElderlyEnables large font, high contrast, and uses TalkBack; performs actions slowly, often double‑taps.Detects accessibility failures: low contrast, missing content descriptions, touch targets too small, or TalkBack not announcing new messages.
AccessibilityForces TalkBack, switch control, and enables captions for media.Verifies that all dynamic content (incoming messages, typing indicators) is announced, and that media controls are operable without sight.
Power UserUses keyboard shortcuts (if available), copies/pastes large texts, attempts to send messages with maximum allowed length, and repeatedly opens/closes the attachment picker.Tests boundary limits, clipboard handling, and performance under heavy load.
AdversarialEnters emojis, zero‑width joiner characters, very long strings (> 10 000 chars), attempts SQL‑injection‑like payloads in text fields, and rapidly toggles airplane mode.Uncovers sanitization gaps, potential injection points, buffer overflows in native modules, and improper handling of malformed Unicode.
Security‑ConsciousChecks for screenshot blocking, inspects notification previews, tries to copy message content to clipboard, and attempts to paste into other apps.Confirms that privacy controls (notification hiding, screenshot flag, clipboard clearing) work as intended.

Running SUSA with each of these personas overnight yields a comprehensive set of observations that can be triaged into actionable bugs.

What SUSA Finds That Scripts Miss

  1. Timing‑dependent UI glitches – A script may wait a fixed 2 seconds for a message to appear; an impatient persona might tap send again after 300 ms, revealing a duplicate‑send bug that the script never triggers because its wait time masks the race.
  2. Accessibility regressions after UI refresh – When the chat list adapts to new message bubbles, TalkBack may lose focus. Scripts that assert static text presence won’t notice the lost focus, but an elderly persona navigating with swipe gestures will repeatedly lose track of where they are.
  3. Combination of settings – Dark mode + 200 % font size + TalkBack can cause a message bubble’s background color to fall below contrast thresholds. Only a persona that enables all three settings together will surface the violation.
  4. Notification interaction quirks – Tapping a notification while the app is in the background may launch a deep link that bypasses the login flow, leaving the user in a logged‑out state. Scripts that start from a logged‑in foreground activity never see this path.
  5. Dead‑end screens – An error dialog that appears after a failed media upload might lack a “Retry” button, trapping the user. Susa’s graph‑based exploration will mark that screen as a dead end and flag it for review.

Integrating SUSA into CI

Add a lightweight step to your CI pipeline that runs Susa with a subset of personas on every pull request. Example using the CLI:


# Install the agent (once per runner)
pip install susatest-agent

# Run a 5‑minute exploration with the impatient and accessibility personas
susatest explore \
    --apk path/to/app-release.apk \
    --personas impatient,accessibility \
    --duration 5m \
    --output susa_report.json \
    --fail-on-crash \
    --fail-on-anr \
    --fail-on-accessibility

The step fails the build if any crash, ANR, or WCAG violation is detected, giving immediate feedback. Over time, you can increase the duration or add more personas to deepen coverage without writing additional test code.

Checklist for Chat Testing

Use this concise list before marking a chat feature as ready for release. Each item can be mapped to one or more test cases from the matrix above.

Run through the checklist on a matrix of devices (different API levels, manufacturers, and form factors) to catch hardware‑specific regressions.

Closing Takeaways

Testing chat on Android is a multidimensional effort that blends functional correctness, concurrency safety, accessibility, and security. Start with a solid foundation of unit and integration tests to guard the core messaging logic. Layer Espresso scripts for the primary UI flows, supplement them with UiAutomator2 for system‑level interactions, and inject deterministic network behavior via MockWebServer to exercise error paths without flakiness.

Performance validation—using the Android Profiler or Systrace—ensures that the scrolling experience remains smooth as conversation histories grow. Accessibility checks must go beyond automated contrast scanners; they require real interaction with TalkBack, font scaling, and switch controls to confirm that every user can perceive and act on new messages.

Security and privacy testing should verify that encryption keys, message contents, and metadata never leak through logs, screenshots, or the clipboard, and that any platform‑provided protections (notification hiding, screenshot blocking) are correctly configured.

Finally, augment your scripted suite with autonomous, persona‑driven exploration. Tools like SUSA simulate the varied ways real users—impatient, curious, elderly, or adversarial—interact with your chat screen, surfacing timing bugs, accessibility regressions, and dead ends that no predetermined test script would ever consider. By combining deterministic automation with stochastic, persona‑based testing, you gain confidence that your chat functionality will withstand the chaos of real‑world usage while delivering a reliable, inclusive, and secure experience.

Keep the checklist handy, iterate on your test matrix as new chat features land, and let each release cycle improve both the depth and breadth of your validation. Your users will notice the difference in stability, trust, and satisfaction. Happy testing.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free