Skip to content

Mobile App Testing — iOS & Android Manual & Automation Guide

DodaTech Updated 2026-06-24 4 min read

Mobile app testing presents unique challenges — device fragmentation, OS version differences, network variability, touch interactions, and platform-specific behaviors that do not exist in web testing. In this guide, you will learn manual testing workflows for iOS and Android, automated UI testing with Espresso and XCUITest, strategies for handling device fragmentation, cloud device lab integration, and mobile CI/CD pipelines. DodaZIP's mobile app goes through 200+ device combinations on BrowserStack App Live before every release.

Learning Path

flowchart LR
  A[Appium Basics] --> B[Mobile Testing Concepts]
  B --> C[Mobile Manual & Automation
You are here] C --> D[Device Cloud Testing] D --> E[Mobile CI/CD] style C fill:#f90,color:#fff

Mobile Testing Types

Test Type What It Covers Tools
Functional Features work correctly Espresso, XCUITest
UI Visual elements and layout Robolectric, Snapshot tests
Performance Startup time, memory, battery Xcode Instruments, Profiler
Network Offline mode, slow networks Network Link Conditioner
Compatibility Device/OS fragmentation BrowserStack, Firebase Test Lab
Installation First-run experience Manual testing

Android UI Testing with Espresso

// LoginActivityTest.kt
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*

@RunWith(AndroidJUnit4::class)
class LoginActivityTest {

    @get:Rule
    var activityRule = ActivityScenarioRule(LoginActivity::class.java)

    @Test
    fun testSuccessfulLogin() {
        onView(withId(R.id.email_input))
            .perform(typeText("user@example.com"))
        onView(withId(R.id.password_input))
            .perform(typeText("password123"))
        onView(withId(R.id.login_button))
            .perform(click())

        onView(withText("Welcome back!"))
            .check(matches(isDisplayed()))
    }

    @Test
    fun testEmptyFieldsShowErrors() {
        onView(withId(R.id.login_button))
            .perform(click())

        onView(withText("Email is required"))
            .check(matches(isDisplayed()))
        onView(withText("Password is required"))
            .check(matches(isDisplayed()))
    }
}

iOS UI Testing with XCUITest

// LoginTests.swift
import XCTest

class LoginTests: XCTestCase {
    let app = XCUIApplication()

    override func setUp() {
        continueAfterFailure = false
        app.launch()
    }

    func testSuccessfulLogin() {
        let emailField = app.textFields["emailInput"]
        emailField.tap()
        emailField.typeText("user@example.com")

        let passwordField = app.secureTextFields["passwordInput"]
        passwordField.tap()
        passwordField.typeText("password123")

        app.buttons["loginButton"].tap()

        let welcomeLabel = app.staticTexts["Welcome back!"]
        XCTAssertTrue(welcomeLabel.waitForExistence(timeout: 5))
    }

    func testInvalidEmailShowsError() {
        let emailField = app.textFields["emailInput"]
        emailField.tap()
        emailField.typeText("invalid")

        let passwordField = app.secureTextFields["passwordInput"]
        passwordField.tap()
        passwordField.typeText("pw")

        app.buttons["loginButton"].tap()

        let errorLabel = app.staticTexts["Invalid email format"]
        XCTAssertTrue(errorLabel.exists)
    }
}

Manual Testing Checklist

## Pre-Release Manual Test Checklist

### Installation
- [ ] Fresh install from app store
- [ ] Update from previous version
- [ ] Install on low-storage device

### Network Conditions
- [ ] WiFi connection
- [ ] Cellular (4G/5G)
- [ ] Offline mode
- [ ] Airplane mode
- [ ] Slow network (throttled)
- [ ] Network switching (WiFi to cellular)

### Device Variations
- [ ] Latest iPhone with latest iOS
- [ ] iPhone 2-3 generations old
- [ ] Latest Android flagship
- [ ] Budget Android device
- [ ] Tablet (iPad/Android)
- [ ] Foldable device

### Orientation
- [ ] Portrait
- [ ] Landscape
- [ ] Rotation during active operation

### Interruptions
- [ ] Incoming call during app use
- [ ] SMS received
- [ ] Push notification arrives
- [ ] App switcher
- [ ] Lock/unlock device

Cloud Device Lab Testing

Run tests across hundreds of real devices in the cloud:

import requests

FIREBASE_API = "https://firebase-testlab.googleapis.com/v1"
PROJECT_ID = "my-project"

def run_on_device_matrix():
    matrix = {
        "testMatrix":
            {"androidInstrumentationTest":
                 {"testApk": {"gcsPath": "gs://my-bucket/app-debug-test.apk"},
                  "appApk": {"gcsPath": "gs://my-bucket/app-debug.apk"}
                  }
             },
        "environmentMatrix":
            {"androidDeviceList":
                 {"androidDevices": [
                     {"androidModelId": "Pixel3", "androidVersionId": "29"},
                     {"androidModelId": "Pixel6", "androidVersionId": "33"},
                     {"androidModelId": "SamsungGalaxyS22", "androidVersionId": "33"},
                     {"androidModelId": "PixelTablet", "androidVersionId": "34"},
                 ]
                  }
             }
    }
    r = requests.post(
        f"{FIREBASE_API}/projects/{PROJECT_ID}/testMatrices",
        json=matrix
    )
    print(f"Test matrix submitted: {r.json()}")

run_on_device_matrix()

Practice Questions

1. What are the main challenges in mobile app testing compared to web testing?

Device fragmentation, OS version differences, touch interactions, network variability, hardware-specific features, and platform-specific APIs.

2. What is the difference between Espresso and XCUITest?

Espresso is Android's UI testing framework written in Kotlin/Java. XCUITest is Apple's UI testing framework written in Swift. Both automate user interactions and assertions.

3. Why should you test on real devices in addition to emulators?

Emulators cannot reproduce hardware-specific issues — camera quality, battery drain, touch latency, GPS accuracy, and push notification reliability.

4. What is Firebase Test Lab and when should you use it?

Firebase Test Lab runs your Android and iOS tests on hundreds of real device/OS combinations in Google's data centers, ideal for pre-release compatibility testing.

Challenge: Build a mobile test strategy for a file-sharing app. Write Espresso tests for Android (file upload, share dialog, download progress). Write XCUITest equivalents for iOS. Create a manual test checklist covering network conditions, interruptions, and device variations. Set up Firebase Test Lab with a 20-device matrix.

FAQ

What is mobile testing?

Mobile testing verifies that an iOS or Android app works correctly across devices, OS versions, network conditions, and usage scenarios.

Should I prioritize manual or automated mobile testing?

Both. Use automation for regression and smoke tests. Use manual testing for exploratory, usability, and real-world network/device testing.

How many devices should I test on?

Test on the top 10-20 devices from your analytics. Include at least one high-end, mid-range, and budget device per platform.

What is the best approach for testing push notifications?

Test: receiving notifications in foreground and background, tapping notification opens correct screen, notification grouping, and notification settings management.

What's Next

Cross-Browser Testing Guide
Cross-Browser Testing
CI/CD Testing Pipeline

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro