android-tdd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Android Test-Driven Development (TDD)

Android测试驱动开发(TDD)

Overview

概述

TDD is a development process where you write tests before feature code, following the Red-Green-Refactor cycle. Every feature starts with a failing test, gets minimal implementation, then is refined.
Core Principle: No production code without a failing test first.
Icon Policy: If UI code is generated as part of TDD, use custom PNG icons and maintain
PROJECT_ICONS.md
(see
android-custom-icons
).
Report Table Policy: If UI tests cover reports that can exceed 25 rows, the UI must use table layouts (see
android-report-tables
).
TDD是一种先编写测试代码,再编写功能代码的开发流程,遵循红-绿-重构循环。每个功能都从编写一个失败的测试开始,完成最小化实现后再进行优化。
核心原则: 没有先编写失败的测试,就不要编写生产代码。
图标规范: 如果UI代码是通过TDD生成的,请使用自定义PNG图标并维护
PROJECT_ICONS.md
(参考
android-custom-icons
)。
报表表格规范: 如果UI测试覆盖的报表行数可能超过25行,UI必须使用表格布局(参考
android-report-tables
)。

Quick Reference

快速参考

TopicReference FileWhen to Use
TDD Workflow
references/tdd-workflow.md
Step-by-step Red-Green-Refactor with examples
Testing by Layer
references/testing-by-layer.md
Unit, integration, persistence, network, UI tests
Advanced Techniques
references/advanced-techniques.md
Factories, behavior verification, LiveData/Flow
Tools & CI Setup
references/tools-and-ci.md
Dependencies, CI pipelines, test configuration
Team Adoption
references/team-adoption.md
Legacy code, team onboarding, troubleshooting
主题参考文件使用场景
TDD工作流
references/tdd-workflow.md
带示例的红-绿-重构分步指南
分层测试
references/testing-by-layer.md
单元测试、集成测试、持久化测试、网络测试、UI测试
高级技巧
references/advanced-techniques.md
工厂模式、行为验证、LiveData/Flow相关测试
工具与CI配置
references/tools-and-ci.md
依赖配置、CI流水线、测试环境设置
团队落地
references/team-adoption.md
遗留代码处理、团队培训、问题排查

The Red-Green-Refactor Cycle

红-绿-重构循环

1. RED    → Write a failing test for desired behavior
2. GREEN  → Write MINIMUM code to make it pass
3. REFACTOR → Clean up while keeping tests green
4. REPEAT → Next behavior
Critical Rules:
  • Never skip the Red phase (verify the test actually fails)
  • Never write more code than needed in Green phase
  • Never refactor with failing tests
  • Each cycle should take minutes, not hours
1. RED    → 为期望的行为编写一个失败的测试
2. GREEN  → 编写最少的代码让测试通过
3. REFACTOR → 在保持测试通过的前提下优化代码
4. REPEAT → 下一个功能点
关键规则:
  • 绝不要跳过红阶段(验证测试确实会失败)
  • 绿阶段绝不要编写超出需求的代码
  • 绝不要在测试失败时进行重构
  • 每个循环应耗时数分钟,而非数小时

Test Pyramid (70/20/10)

测试金字塔(70/20/10)

        /  UI  \        10% - Espresso, end-to-end flows
       /--------\
      / Integra- \      20% - ViewModel+Repository, Room, API
     /  tion      \
    /--------------\
   /   Unit Tests   \   70% - Pure Kotlin, fast, isolated
  /==================\
TypeSpeedScopeLocationTools
Unit<1ms eachSingle class/method
test/
JUnit, Mockito
Integration~100ms eachComponent interactions
test/
or
androidTest/
JUnit, Robolectric
UI~1s eachUser flows
androidTest/
Espresso, Compose Testing
        /  UI  \\        10% - Espresso、端到端流程测试
       /--------\\
      / 集成测试 \\      20% - ViewModel+Repository、Room、API测试
     /          \\
    /--------------\\
   /   单元测试     \\   70% - 纯Kotlin代码、快速、隔离
  /==================\\
测试类型速度测试范围代码位置工具
单元测试每个<1毫秒单个类/方法
test/
JUnit、Mockito
集成测试每个~100毫秒组件间交互
test/
androidTest/
JUnit、Robolectric
UI测试每个~1秒用户流程
androidTest/
Espresso、Compose Testing

TDD Workflow for Android Features

Android功能的TDD工作流

Step 1: Define the Requirement

步骤1:定义需求

Start with a clear user story or acceptance criteria:
As a user, I want to add items to my cart so I can purchase them later.
从清晰的用户故事或验收标准开始:
作为用户,我希望能将商品加入购物车,以便后续购买。

Step 2: Write the Failing Test (Red)

步骤2:编写失败的测试(红阶段)

kotlin
@Test
fun addItemToCart_increasesCartCount() {
    val cart = ShoppingCart()
    cart.addItem(Product("Phone", 999.99))
    assertEquals(1, cart.itemCount)
}
Run it. It must fail (class doesn't exist yet).
kotlin
@Test
fun addItemToCart_increasesCartCount() {
    val cart = ShoppingCart()
    cart.addItem(Product("Phone", 999.99))
    assertEquals(1, cart.itemCount)
}
运行测试,必须失败(此时
ShoppingCart
类还不存在)。

Step 3: Write Minimal Code (Green)

步骤3:编写最少代码(绿阶段)

kotlin
class ShoppingCart {
    private val items = mutableListOf<Product>()
    fun addItem(product: Product) { items.add(product) }
    val itemCount: Int get() = items.size
}
Run test. It passes. Stop writing code.
kotlin
class ShoppingCart {
    private val items = mutableListOf<Product>()
    fun addItem(product: Product) { items.add(product) }
    val itemCount: Int get() = items.size
}
运行测试,测试通过后停止编写代码。

Step 4: Add Next Test, Then Refactor

步骤4:添加下一个测试,然后重构

kotlin
@Test
fun addMultipleItems_calculatesTotal() {
    val cart = ShoppingCart()
    cart.addItem(Product("Phone", 999.99))
    cart.addItem(Product("Case", 29.99))
    assertEquals(1029.98, cart.totalPrice, 0.01)
}
Implement
totalPrice
, then refactor both test and production code.
kotlin
@Test
fun addMultipleItems_calculatesTotal() {
    val cart = ShoppingCart()
    cart.addItem(Product("Phone", 999.99))
    cart.addItem(Product("Case", 29.99))
    assertEquals(1029.98, cart.totalPrice, 0.01)
}
实现
totalPrice
方法,然后重构测试代码和生产代码。

Layer-Specific Testing Summary

分层测试总结

Unit Tests (Domain & ViewModel)

单元测试(领域层 & ViewModel)

kotlin
class ScoreTest {
    @Test
    fun increment_increasesCurrentScore() {
        val score = Score()
        score.increment()
        assertEquals(1, score.current)
    }
}
  • Mock all dependencies with Mockito
  • Test one behavior per test
  • No Android framework dependencies
kotlin
class ScoreTest {
    @Test
    fun increment_increasesCurrentScore() {
        val score = Score()
        score.increment()
        assertEquals(1, score.current)
    }
}
  • 使用Mockito模拟所有依赖
  • 每个测试验证一个行为
  • 不依赖Android框架

Integration Tests (Repository + Database)

集成测试(仓库层 + 数据库)

kotlin
@RunWith(AndroidJUnit4::class)
class WishlistDaoTest {
    private lateinit var db: AppDatabase

    @Before
    fun setup() {
        db = Room.inMemoryDatabaseBuilder(
            ApplicationProvider.getApplicationContext(),
            AppDatabase::class.java
        ).build()
    }

    @After
    fun teardown() { db.close() }
}
kotlin
@RunWith(AndroidJUnit4::class)
class WishlistDaoTest {
    private lateinit var db: AppDatabase

    @Before
    fun setup() {
        db = Room.inMemoryDatabaseBuilder(
            ApplicationProvider.getApplicationContext(),
            AppDatabase::class.java
        ).build()
    }

    @After
    fun teardown() { db.close() }
}

Network Tests (API Layer)

网络测试(API层)

kotlin
class ApiServiceTest {
    private val mockWebServer = MockWebServer()

    @Test
    fun fetchData_returnsExpectedResponse() {
        mockWebServer.enqueue(
            MockResponse().setBody("""{"id":1,"name":"Test"}""").setResponseCode(200)
        )
        val response = service.fetchData().execute()
        assertEquals("Test", response.body()?.name)
    }
}
kotlin
class ApiServiceTest {
    private val mockWebServer = MockWebServer()

    @Test
    fun fetchData_returnsExpectedResponse() {
        mockWebServer.enqueue(
            MockResponse().setBody("""{"id":1,"name":"Test"}""").setResponseCode(200)
        )
        val response = service.fetchData().execute()
        assertEquals("Test", response.body()?.name)
    }
}

UI Tests (Espresso / Compose)

UI测试(Espresso / Compose)

kotlin
@Test
fun clickSaveButton_showsConfirmation() {
    onView(withId(R.id.saveButton)).perform(click())
    onView(withText("Saved!")).check(matches(isDisplayed()))
}
kotlin
@Test
fun clickSaveButton_showsConfirmation() {
    onView(withId(R.id.saveButton)).perform(click())
    onView(withText("Saved!")).check(matches(isDisplayed()))
}

Essential Test Dependencies

必备测试依赖

groovy
dependencies {
    // Unit
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'

    // Integration & UI
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation 'androidx.arch.core:core-testing:2.2.0'

    // Room & Network
    testImplementation 'androidx.room:room-testing:2.6.1'
    testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
}
groovy
dependencies {
    // 单元测试
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'

    // 集成测试 & UI测试
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation 'androidx.arch.core:core-testing:2.2.0'

    // Room & 网络测试
    testImplementation 'androidx.room:room-testing:2.6.1'
    testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
}

Test Naming Convention

测试命名规范

Use descriptive names following:
methodUnderTest_condition_expectedResult
kotlin
fun addItem_emptyCart_cartHasOneItem()
fun calculateTotal_multipleItems_returnsSumOfPrices()
fun login_invalidCredentials_returnsError()
fun fetchUsers_networkError_showsErrorState()
使用以下格式的描述性名称:
被测方法_测试条件_预期结果
kotlin
fun addItem_emptyCart_cartHasOneItem()
fun calculateTotal_multipleItems_returnsSumOfPrices()
fun login_invalidCredentials_returnsError()
fun fetchUsers_networkError_showsErrorState()

Patterns & Anti-Patterns

最佳实践与反模式

DO

应该

  • Write tests first (always Red before Green)
  • Keep tests small and focused (one assertion per concept)
  • Use descriptive test names that document behavior
  • Use test data factories for complex objects
  • Test edge cases and error conditions
  • Refactor tests alongside production code
  • 先编写测试(始终遵循先红后绿)
  • 保持测试小巧且聚焦(每个测试验证一个核心逻辑)
  • 使用描述性的测试名称来记录功能行为
  • 为复杂对象使用测试数据工厂
  • 测试边缘情况和错误场景
  • 同步重构测试代码和生产代码

DON'T

不应该

  • Test implementation details (test behavior, not internals)
  • Write tests for generated code (Hilt, Room DAOs)
  • Test third-party libraries (Retrofit, Gson)
  • Chase 100% coverage at expense of test quality
  • Write slow, flaky, or order-dependent tests
  • Skip the Red phase (you won't catch false positives)
  • 测试实现细节(测试行为,而非内部逻辑)
  • 为生成代码编写测试(Hilt、Room DAOs)
  • 测试第三方库(Retrofit、Gson)
  • 为了追求100%覆盖率而牺牲测试质量
  • 编写缓慢、不稳定或依赖执行顺序的测试
  • 跳过红阶段(无法发现假阳性结果)

Integration with Other Skills

与其他技能的集成

feature-planning → Define specs & acceptance criteria
android-tdd → Write tests first, then implement (THIS SKILL)
android-development → Follow architecture & Kotlin standards
ai-error-handling → Validate AI-generated implementations
vibe-security-skill → Security review
Key Integrations:
  • android-development: Follow MVVM + Clean Architecture for testable design
  • feature-planning: Use acceptance criteria as test scenarios
  • ai-error-handling: Validate AI output against test expectations
  • superpowers:test-driven-development: General TDD workflow orchestration
feature-planning → 定义需求规格与验收标准
android-tdd → 先编写测试,再实现功能(当前技能)
android-development → 遵循架构与Kotlin标准
ai-error-handling → 验证AI生成的实现
vibe-security-skill → 安全评审
关键集成点:
  • android-development: 遵循MVVM + 清洁架构以实现可测试性设计
  • feature-planning: 将验收标准作为测试场景
  • ai-error-handling: 根据测试预期验证AI输出
  • superpowers:test-driven-development: 通用TDD工作流编排

CI Pipeline

CI流水线

yaml
name: Android TDD
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Unit Tests
        run: ./gradlew test
      - name: Instrumented Tests
        run: ./gradlew connectedAndroidTest
      - name: Coverage Report
        run: ./gradlew jacocoTestReport
CI Rules:
  • All tests must pass before merge
  • Coverage reports generated on every PR
  • Unit tests and instrumented tests run in parallel
yaml
name: Android TDD
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Unit Tests
        run: ./gradlew test
      - name: Instrumented Tests
        run: ./gradlew connectedAndroidTest
      - name: Coverage Report
        run: ./gradlew jacocoTestReport
CI规则:
  • 所有测试必须通过才能合并代码
  • 每个PR都要生成覆盖率报告
  • 单元测试和仪器化测试并行运行

References

参考资料

  • Google Testing Guide: developer.android.com/training/testing
  • Mockito Kotlin: github.com/mockito/mockito-kotlin
  • Espresso: developer.android.com/training/testing/espresso
  • Architecture Samples: github.com/android/architecture-samples
  • Google测试指南: developer.android.com/training/testing
  • Mockito Kotlin: github.com/mockito/mockito-kotlin
  • Espresso: developer.android.com/training/testing/espresso
  • 架构示例: github.com/android/architecture-samples ",