
Mobile App Testing
- 752 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
mobile-app-testing is a Claude Code skill that runs comprehensive unit, UI, integration, and performance tests on iOS and Android apps for developers who need reliable pre-release mobile test coverage.
About
mobile-app-testing is an aj-geddes/useful-ai-prompts skill that implements comprehensive mobile testing strategies for iOS and Android applications. It covers unit tests, UI tests, integration tests, and performance testing with automation frameworks including Detox, Appium, and XCTest. Developers reach for mobile-app-testing when building reliable mobile apps, automating UI flows, or establishing test coverage before release. The skill provides quick-start guidance, reference guides, and best practices for creating test suites that catch regressions across native mobile stacks. Use it when release readiness depends on structured mobile QA rather than ad hoc manual checks.
- Covers unit tests, UI tests, integration tests, and performance testing
- Supports Detox, Appium, and XCTest for automation across platforms
- Includes regression testing strategies before releases
- Ready-to-use Jest and React Native Testing Library examples
- Works for both iOS and Android mobile applications
Mobile App Testing by the numbers
- 752 all-time installs (skills.sh)
- Ranked #564 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill mobile-app-testingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 752 |
|---|---|
| repo stars | ★ 305 |
| Security audit | 3 / 3 scanners passed |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you automate mobile app testing on iOS and Android?
Run comprehensive unit, UI, integration, and performance tests on iOS and Android apps before every release.
Who is it for?
Mobile developers preparing iOS or Android releases who need structured unit, UI, integration, and performance test automation.
Skip if: Web-only or backend-only projects with no native iOS or Android application to test.
When should I use this skill?
The developer asks to test iOS or Android apps with XCTest, Detox, Appium, or mobile performance and integration coverage.
What you get
Mobile test plan, unit/UI/integration test suites, and performance test configuration for iOS and Android apps.
- Test plan
- UI test suite
- Integration test configuration
Files
Mobile App Testing
Table of Contents
Overview
Implement comprehensive testing strategies for mobile applications including unit tests, UI tests, integration tests, and performance testing.
When to Use
- Creating reliable mobile applications with test coverage
- Automating UI testing across iOS and Android
- Performance testing and optimization
- Integration testing with backend services
- Regression testing before releases
Quick Start
Minimal working example:
// Unit test with Jest
import { calculate } from "../utils/math";
describe("Math utilities", () => {
test("should add two numbers", () => {
expect(calculate.add(2, 3)).toBe(5);
});
test("should handle negative numbers", () => {
expect(calculate.add(-2, 3)).toBe(1);
});
});
// Component unit test
import React from "react";
import { render, screen } from "@testing-library/react-native";
import { UserProfile } from "../components/UserProfile";
describe("UserProfile Component", () => {
test("renders user name correctly", () => {
const mockUser = { id: "1", name: "John Doe", email: "john@example.com" };
render(<UserProfile user={mockUser} />);
expect(screen.getByText("John Doe")).toBeTruthy();
});
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| React Native Testing with Jest & Detox | React Native Testing with Jest & Detox |
| iOS Testing with XCTest | iOS Testing with XCTest |
| Android Testing with Espresso | Android Testing with Espresso |
| Performance Testing | Performance Testing |
Best Practices
✅ DO
- Write tests for business logic first
- Use dependency injection for testability
- Mock external API calls
- Test both success and failure paths
- Automate UI testing for critical flows
- Run tests on real devices
- Measure performance on target devices
- Keep tests isolated and independent
- Use meaningful test names
- Maintain >80% code coverage
❌ DON'T
- Skip testing UI-critical flows
- Use hardcoded test data
- Ignore performance regressions
- Test implementation details
- Make tests flaky or unreliable
- Skip testing on actual devices
- Ignore accessibility testing
- Create interdependent tests
- Test without mocking APIs
- Deploy untested code
Android Testing with Espresso
Android Testing with Espresso
@RunWith(AndroidJUnit4::class)
class UserViewModelTest {
private lateinit var viewModel: UserViewModel
private val mockApiService = mock<ApiService>()
@Before
fun setUp() {
viewModel = UserViewModel(mockApiService)
}
@Test
fun fetchUserSuccess() = runTest {
val expectedUser = User("1", "John", "john@example.com")
`when`(mockApiService.getUser("1")).thenReturn(expectedUser)
viewModel.fetchUser("1")
assertEquals(expectedUser.name, viewModel.user.value?.name)
assertEquals(null, viewModel.errorMessage.value)
}
@Test
fun fetchUserFailure() = runTest {
`when`(mockApiService.getUser("1"))
.thenThrow(IOException("Network error"))
viewModel.fetchUser("1")
assertEquals(null, viewModel.user.value)
assertNotNull(viewModel.errorMessage.value)
}
}
// UI Test with Espresso
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Test
fun testLoginWithValidCredentials() {
onView(withId(R.id.emailInput))
.perform(typeText("user@example.com"))
onView(withId(R.id.passwordInput))
.perform(typeText("password123"))
onView(withId(R.id.loginButton))
.perform(click())
onView(withText("Home"))
.check(matches(isDisplayed()))
}
@Test
fun testLoginWithInvalidCredentials() {
onView(withId(R.id.emailInput))
.perform(typeText("invalid@example.com"))
onView(withId(R.id.passwordInput))
.perform(typeText("wrongpassword"))
onView(withId(R.id.loginButton))
.perform(click())
onView(withText(containsString("Invalid credentials")))
.check(matches(isDisplayed()))
}
@Test
fun testNavigationBetweenTabs() {
onView(withId(R.id.profileTab)).perform(click())
onView(withText("Profile")).check(matches(isDisplayed()))
onView(withId(R.id.homeTab)).perform(click())
onView(withText("Home")).check(matches(isDisplayed()))
}
}iOS Testing with XCTest
iOS Testing with XCTest
import XCTest
@testable import MyApp
class UserViewModelTests: XCTestCase {
var viewModel: UserViewModel!
var mockNetworkService: MockNetworkService!
override func setUp() {
super.setUp()
mockNetworkService = MockNetworkService()
viewModel = UserViewModel(networkService: mockNetworkService)
}
func testFetchUserSuccess() async {
let expectedUser = User(id: UUID(), name: "John", email: "john@example.com")
mockNetworkService.mockUser = expectedUser
await viewModel.fetchUser(id: expectedUser.id)
XCTAssertEqual(viewModel.user?.name, "John")
XCTAssertNil(viewModel.errorMessage)
XCTAssertFalse(viewModel.isLoading)
}
func testFetchUserFailure() async {
mockNetworkService.shouldFail = true
await viewModel.fetchUser(id: UUID())
XCTAssertNil(viewModel.user)
XCTAssertNotNil(viewModel.errorMessage)
XCTAssertFalse(viewModel.isLoading)
}
}
class MockNetworkService: NetworkService {
var mockUser: User?
var shouldFail = false
override func fetch<T: Decodable>(
_: T.Type,
from endpoint: String
) async throws -> T {
if shouldFail {
throw NetworkError.unknown
}
return mockUser as! T
}
}
// UI Test
class LoginUITests: XCTestCase {
override func setUp() {
super.setUp()
continueAfterFailure = false
XCUIApplication().launch()
}
func testLoginFlow() {
let app = XCUIApplication()
let emailTextField = app.textFields["emailInput"]
let passwordTextField = app.secureTextFields["passwordInput"]
let loginButton = app.buttons["loginButton"]
emailTextField.tap()
emailTextField.typeText("user@example.com")
passwordTextField.tap()
passwordTextField.typeText("password123")
loginButton.tap()
let homeText = app.staticTexts["Home Feed"]
XCTAssertTrue(homeText.waitForExistence(timeout: 5))
}
func testNavigationBetweenTabs() {
let app = XCUIApplication()
let profileTab = app.tabBars.buttons["Profile"]
let homeTab = app.tabBars.buttons["Home"]
profileTab.tap()
XCTAssertTrue(app.staticTexts["Profile"].exists)
homeTab.tap()
XCTAssertTrue(app.staticTexts["Home"].exists)
}
}Performance Testing
Performance Testing
import XCTest
class PerformanceTests: XCTestCase {
func testListRenderingPerformance() {
let viewModel = ItemsViewModel()
viewModel.items = (0..<1000).map { i in
Item(id: UUID(), title: "Item \(i)", price: Double(i))
}
measure {
_ = viewModel.items.filter { $0.price > 50 }
}
}
func testNetworkResponseTime() {
let networkService = NetworkService()
measure {
let expectation = XCTestExpectation(description: "Fetch user")
Task {
do {
_ = try await networkService.fetch(User.self, from: "/users/test")
expectation.fulfill()
} catch {
XCTFail("Network request failed")
}
}
wait(for: [expectation], timeout: 10)
}
}
}React Native Testing with Jest & Detox
React Native Testing with Jest & Detox
// Unit test with Jest
import { calculate } from "../utils/math";
describe("Math utilities", () => {
test("should add two numbers", () => {
expect(calculate.add(2, 3)).toBe(5);
});
test("should handle negative numbers", () => {
expect(calculate.add(-2, 3)).toBe(1);
});
});
// Component unit test
import React from "react";
import { render, screen } from "@testing-library/react-native";
import { UserProfile } from "../components/UserProfile";
describe("UserProfile Component", () => {
test("renders user name correctly", () => {
const mockUser = { id: "1", name: "John Doe", email: "john@example.com" };
render(<UserProfile user={mockUser} />);
expect(screen.getByText("John Doe")).toBeTruthy();
});
test("handles missing user gracefully", () => {
render(<UserProfile user={null} />);
expect(screen.getByText(/no user data/i)).toBeTruthy();
});
});
// E2E Testing with Detox
describe("Login Flow E2E Test", () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it("should login successfully with valid credentials", async () => {
await waitFor(element(by.id("emailInput")))
.toBeVisible()
.withTimeout(5000);
await element(by.id("emailInput")).typeText("user@example.com");
await element(by.id("passwordInput")).typeText("password123");
await element(by.id("loginButton")).multiTap();
await waitFor(element(by.text("Home Feed")))
.toBeVisible()
.withTimeout(5000);
});
it("should show error with invalid credentials", async () => {
await element(by.id("emailInput")).typeText("invalid@example.com");
await element(by.id("passwordInput")).typeText("wrongpass");
await element(by.id("loginButton")).multiTap();
await waitFor(element(by.text(/invalid credentials/i)))
.toBeVisible()
.withTimeout(5000);
});
it("should navigate between tabs", async () => {
await element(by.id("profileTab")).tap();
await waitFor(element(by.text("Profile")))
.toBeVisible()
.withTimeout(2000);
await element(by.id("homeTab")).tap();
await waitFor(element(by.text("Home Feed")))
.toBeVisible()
.withTimeout(2000);
});
});#!/bin/bash
# scaffold-tests.sh - Generate test file scaffolding
# Usage: ./scaffold-tests.sh <source_file> [--framework jest|pytest|mocha]
set -euo pipefail
SOURCE_FILE="${{1:?Usage: $0 <source_file> [--framework jest|pytest|mocha]}}"
FRAMEWORK="${{2:-jest}}"
echo "Scaffolding tests for: $SOURCE_FILE (framework: $FRAMEWORK)"
# TODO: Implement test scaffolding logic
# - Parse source file for exported functions/classes
# - Generate test stubs for each export
# - Include setup/teardown boilerplate
# - Add common assertion patterns
echo "Test scaffolding complete."
// Test Template
// TODO: Customize for your testing framework and project
describe('ModuleName', () => {
// Setup
beforeEach(() => {
// TODO: Add test setup
});
afterEach(() => {
// TODO: Add cleanup
});
describe('functionName', () => {
it('should handle the happy path', () => {
// TODO: Add assertion
});
it('should handle edge cases', () => {
// TODO: Add edge case tests
});
it('should handle errors gracefully', () => {
// TODO: Add error handling tests
});
});
});
Related skills
How it compares
Pick this over web E2E skills when the target is native iOS/Android stacks with XCTest, Detox, or Appium.
FAQ
Which mobile test frameworks does mobile-app-testing cover?
mobile-app-testing covers XCTest for iOS, Detox and Appium for cross-platform UI automation, plus strategies for unit, integration, and performance testing on iOS and Android. The skill helps developers choose and implement frameworks for pre-release coverage.
When should developers use mobile-app-testing?
mobile-app-testing applies when building reliable native or cross-platform mobile apps that need automated test coverage before release. Use it for UI flow automation, integration testing, and performance validation rather than manual-only QA.
Is Mobile App Testing safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.