Mobile App Wiki

Mobile App Wiki

mobileapp.wiki

Home

Categories

mobileapp.wiki

Mobile App Wiki

Mobile app development knowledge base

PrivacyHomeSitemapRSS
© 2026 mobileapp.wiki
Home/Testing/Unit Testing for Mobile Apps: Practical Guide for iOS and Android
Testing5 min read

Unit Testing for Mobile Apps: Practical Guide for iOS and Android

Write effective unit tests for iOS and Android using XCTest, JUnit, and modern frameworks. Covers mocking, architecture, and code coverage strategies.

unit testingxctestjunitmockitotest-driven developmentcode coveragemobile testing

Table of Contents

What Is Unit Testing?Why Unit Testing Matters for MobileiOS Unit Testing with XCTestBasic StructureAsync TestingSwift Testing Framework (2026)Android Unit Testing with JUnitLocal vs. Instrumented TestsKey Android Testing LibrariesWhat to Test (and What Not to Test)High-Value Test TargetsLow-Value Test TargetsMocking and Dependency InjectionCommon Mocking ApproachesCode CoverageRunning Tests in CI/CDRelated Topics

What Is Unit Testing?

A unit test verifies that a single, isolated piece of code (a function, method, or class) behaves correctly. It runs fast (milliseconds), does not depend on external systems (no network, no database, no file system), and produces the same result every time.

Unit tests form the base of the testing pyramid. They are the cheapest to write, fastest to run, and most reliable to maintain. A healthy mobile codebase typically has hundreds or thousands of unit tests that execute in seconds.

Why Unit Testing Matters for Mobile

Mobile development has unique pressures that make unit testing especially valuable:

  • Slow feedback loops. Building and deploying to a device takes 30-120 seconds. Unit tests give feedback in under 5 seconds.
  • Expensive manual testing. Testing every feature on multiple devices and OS versions manually is unsustainable.
  • Frequent regressions. Mobile apps evolve quickly. Without tests, fixing one bug often introduces another.
  • Store review delays. A critical bug that reaches production requires a hotfix, a new review, and 24-48 hours before users get the patch. Prevention through testing is far cheaper.

iOS Unit Testing with XCTest

XCTest is Apple's built-in testing framework, included with Xcode. No additional dependencies needed.

Basic Structure

A test case is a class that inherits from XCTestCase. Each test method starts with the word "test" and uses assertion functions to verify expectations.

Key assertions:

  • XCTAssertEqual(a, b) - Values are equal
  • XCTAssertTrue(expr) - Expression is true
  • XCTAssertNil(value) - Value is nil
  • XCTAssertThrowsError(expr) - Expression throws
  • XCTAssertEqual(a, b, accuracy: 0.01) - Floating point comparison

Async Testing

Modern Swift code uses async/await extensively. XCTest supports async test methods directly. Mark your test function as async and use await normally. For callback-based APIs, use XCTestExpectation with a timeout.

Swift Testing Framework (2026)

Starting with Xcode 26, Apple's newer Swift Testing framework offers a more modern syntax with the @Test macro, parameterized tests, and better integration with Swift concurrency. For new projects, consider adopting Swift Testing alongside XCTest.

Android Unit Testing with JUnit

Android uses JUnit as the foundation. Most teams use JUnit 5 (Jupiter) in 2026, though JUnit 4 remains widely supported.

Local vs. Instrumented Tests

Local unit tests run on your development machine's JVM. They are fast (no emulator needed) and live in the test/ directory. Use these for business logic, view models, repositories, and utility functions.

Instrumented tests run on a device or emulator. They live in the androidTest/ directory. Use these only when you need actual Android framework APIs (Context, SharedPreferences, Room database).

For unit testing, prefer local tests whenever possible. They are 10-100x faster.

Key Android Testing Libraries

  • JUnit 5 - Test framework with annotations, assertions, and lifecycle hooks
  • Mockito / MockK - Mocking frameworks (MockK is Kotlin-first and increasingly preferred)
  • Truth - Google's fluent assertion library (more readable than JUnit assertions)
  • Turbine - Testing library for Kotlin Flows (essential for MVVM architectures)
  • Robolectric - Simulates Android framework on JVM so you can test Android-specific code without an emulator

What to Test (and What Not to Test)

High-Value Test Targets

Business logic and domain models. Validation rules, calculations, data transformations, and state machines. These are pure functions with clear inputs and outputs.

ViewModels / Presenters. Test that user actions produce the correct state changes. Mock the repository layer and verify the ViewModel emits the expected UI states.

Repository and data layer. Test that network responses are correctly parsed, cached, and transformed. Mock the network client and verify data mapping.

Utility functions. Date formatters, string manipulators, currency converters, and other helpers that the entire app depends on.

Low-Value Test Targets

UI layout code. Testing that a button has the correct color or position is brittle and better covered by snapshot tests or manual review.

Simple data classes. A data class with no logic does not need its own test.

Platform framework code. Do not test that UIKit or Android SDK works correctly. Test your code, not Apple's or Google's.

Mocking and Dependency Injection

Unit tests must be isolated. If your function calls a network API, the test should not actually make a network request. This is where mocking comes in.

The pattern:

  1. Define dependencies as interfaces (protocols in Swift, interfaces in Kotlin)
  2. Inject dependencies through the constructor
  3. In tests, provide mock implementations that return controlled data

This approach (dependency injection) is not just a testing technique. It produces better-architected code that is easier to understand and modify.

Common Mocking Approaches

Manual mocks: Create a simple class that conforms to the protocol and stores method calls. Best for simple dependencies.

Mocking frameworks: Use Mockito/MockK (Android) or swift-testing-mock libraries. Best for complex dependencies with many methods.

Fakes: An in-memory implementation of a real dependency (e.g., an in-memory database). Best when the behavior matters, not just the method calls.

Code Coverage

Code coverage measures what percentage of your code is executed during tests. It is a useful directional metric but not a quality indicator.

Reasonable targets:

  • Business logic layer: 80-90%
  • ViewModel/Presenter layer: 70-80%
  • Data layer: 60-70%
  • Overall project: 60-70%

The 100% coverage trap: Chasing 100% coverage leads to brittle, low-value tests that verify implementation details rather than behavior. A project with 65% meaningful coverage is healthier than one with 95% coverage padded with trivial assertions.

Running Tests in CI/CD

Integrate unit tests into your continuous integration pipeline:

  • Run all unit tests on every pull request
  • Block merging if any test fails
  • Track coverage trends over time (not as a gate, but as a dashboard)
  • Keep total test execution under 5 minutes to maintain fast feedback

Popular CI services for mobile: GitHub Actions, Bitrise, CircleCI, Codemagic, and Xcode Cloud (iOS only).

Related Topics

  • E2E Testing Guide
  • Performance Profiling Guide
  • Regression Testing and Release QA

How did you find this article?

Share

← Previous

Beta Testing for Mobile Apps: A Complete Guide

Next →

Crash Reporting for Mobile Apps: Tools, Strategies, and Best Practices

Related Articles

Beta Testing for Mobile Apps: A Complete Guide

Learn how to run effective beta tests using TestFlight and Google Play testing tracks. Recruit testers, collect feedback, and ship with confidence.

Crash Reporting for Mobile Apps: Tools, Strategies, and Best Practices

Set up effective crash reporting with Firebase Crashlytics, Sentry, and Bugsnag. Learn how to prioritize, debug, and reduce crash rates in production apps.

In-App A/B Testing: Run Experiments That Drive Real Results

Design, run, and analyze in-app A/B tests for mobile. Covers feature flags, statistical significance, common pitfalls, and the best experimentation tools.

Performance Profiling for Mobile Apps: Find and Fix Bottlenecks

Master performance profiling with Xcode Instruments, Android Studio Profiler, and Flipper. Learn to diagnose slow renders, memory leaks, and battery drain.

End-to-End Testing for Mobile Apps: Automate User Journeys

Set up end-to-end mobile testing with Detox, Maestro, XCUITest, and Espresso. Automate critical flows and integrate E2E tests into your CI pipeline.