编写第一个单元测试:Android Studio环境搭建,创建并运行简单测试

说实话,很多做Android开发的朋友第一次接触单元测试时,心里会犯嘀咕:这东西到底有没有用?我的经验是——不仅有用,而且越早写越省钱。我在上一个项目中,就因为生产环境漏了一个空指针,结果线上崩了三天才修好。从那以后,我再也不敢轻视单元测试了。

核心观点:单元测试不是“测试人员的活”,而是开发者保质量的最后一道防线。你写的每一条测试,都是在给未来的自己留一条退路。

1. 环境搭建 —— 别嫌麻烦,一步到位

单元测试跑在JVM上,不需要模拟器或真机。所以咱们的环境其实挺轻量。我习惯用Android Studio自带的测试工具链,省心。

  1. JDK 8+ —— 推荐JDK 11,兼容性好。我遇到过JDK 8在某些Gradle版本下报错,后来统一升到11就再没出过事。
  2. Android Studio 4.0+ —— 随便哪个版本都行,但最好用最新稳定版。我记得有一次用预览版跑测试,Mockito死活不认 @Mock 注解,折腾了一下午才发现是IDE bug。
  3. Gradle 7.0+ —— 项目默认的就行。不过要注意,Kotlin项目如果混合Java测试,记得在 build.gradle 里加上 testOptions.unitTests.includeAndroidResources = true。

我的小技巧:新建项目时直接勾选「Include Kotlin support」和「Include tests」。这样 Android Studio 会自动生成 test/ 目录和 JUnit 依赖,省去你手动配置的功夫。

打开 Android Studio,File → New → New Project,选「Empty Activity」,然后下一步下一步。创建成功后,你会看到下面这个目录结构:

app/
├── src/
│   ├── main/java/   ← 生产代码
│   ├── test/java/   ← 单元测试(跑在JVM上)
│   └── androidTest/java/  ← 仪器化测试(需要设备/模拟器)

我们这次只关心 test/ 目录。里面的测试跑得飞快,毫秒级完成。

2. 创建第一个测试类 —— 从写个计算器开始

写个最简单的 Calculator 类,里面就两个方法:加法、除法。你想想看,这种业务逻辑最容易出错,最适合用单元测试来保护。

// src/main/java/com/example/myapp/Calculator.java
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int divide(int a, int b) {
        return a / b;  // 危险!b=0时会抛异常
    }
}

嗯,注意上面的 divide 方法,如果除数为0会抛 ArithmeticException。我们一会儿就用测试把它抓出来。

现在创建测试类。我习惯在 Calculator 类名上右键 → Go To → Test,然后 Android Studio 会弹出窗口,勾选「setUp/@Before」和「tearDown/@After」,测试类名自动生成 CalculatorTest,放在 test/ 目录下。

// src/test/java/com/example/myapp/CalculatorTest.java
import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {

    private Calculator calculator = new Calculator();

    @Test
    public void add_shouldReturnSum() {
        int result = calculator.add(2, 3);
        assertEquals("2 + 3 应该等于5", 5, result);
    }

    @Test(expected = ArithmeticException.class)
    public void divide_shouldThrowWhenDivisorIsZero() {
        calculator.divide(10, 0);
    }
}

这里我用 @Test(expected = ...) 来验证异常。你想想看,如果不写这个测试,可能上线后用户输入个0,App 直接闪退,差评妥妥的。

3. 运行测试 —— 紧张而有趣的一刻

在测试类左侧有个绿色三角形,点击它选择「Run 'CalculatorTest'」。或者用快捷键 Ctrl+Shift+F10(Windows/Linux)或 Cmd+Shift+R(Mac)。

等个两三秒,你会看到测试结果面板:

测试方法 结果 耗时
add_shouldReturnSum ✅ 通过 0.002s
divide_shouldThrowWhenDivisorIsZero ✅ 通过 0.001s

恭喜你,第一个单元测试跑通了!

曾经踩过的坑:我刚开始写测试时,总是忘记在 build.gradle 里加 testImplementation 'junit:junit:4.13.2'。结果运行测试时直接报 ClassNotFoundException: org.junit.Test。更坑的是,Android Studio 有时候不会主动提示你缺依赖,你得手动检查。所以第一步先检查一下 app/build.gradle 的 dependencies 块:

dependencies {
    testImplementation 'junit:junit:4.13.2'
    // 如果要用 Mockito,再加一行
    testImplementation 'org.mockito:mockito-core:4.6.1'
}

4. 理解 JUnit 4 常用注解(附一个表格)

JUnit 4 的注解其实不多,我列个表格帮你快速上手:

注解 作用 执行次数
@Test 标记一个测试方法 每个测试方法执行一次
@Before 每个测试方法运行前执行,用来初始化共用对象 每个测试方法前执行一次
@After 每个测试方法运行后执行,用来清理资源 每个测试方法后执行一次
@BeforeClass 所有测试方法运行前执行一次,必须 static 整个测试类只执行一次
@AfterClass 所有测试方法运行后执行一次,必须 static 整个测试类只执行一次
@Ignore 忽略一个测试方法,不执行 不执行

我个人习惯用 @Before 放对象初始化,比如 new Calculator()。这样每个测试方法拿到的是全新实例,互不干扰。如果你有耗时的资源(比如数据库连接),可以用 @BeforeClass 只初始化一次。

5. 关于「Android环境」的那些事

你可能听说过「单元测试不需要 Android 环境」。这是对的,但有个例外:如果你的代码调用了 Android 框架的 API(比如 Context、Toast),JVM 上没有这些类,测试就会崩。解决方案有两种:

  • 用 Mockito 模拟依赖 —— 我们后面章节会详细讲。
  • 把 Android 依赖抽成接口 —— 更清洁的设计。

不过别慌,你刚才写的 Calculator 完全不用 Android 类,所以直接跑没问题。我第一次写测试时就犯过傻,在测试类里引了个 android.util.Log,结果运行时报 RuntimeException: Stub!。后来才知道,那些是桩方法,只能在真机或模拟器上跑。所以记住:test/ 目录下的测试,不要依赖 Android SDK。如果非要用,请用 androidTest/ 目录。

6. 全流程示意图

下面这张图帮你把从环境到运行测试的步骤串起来,方便你对照操作:

创建并运行第一个单元测试的流程 环境准备 1. 安装JDK 11+ 2. Android Studio 创建项目 测试开发 3. 检查Gradle依赖 4. 编写生产代码 5. 编写测试类 运行与验证 6. 运行测试 7. 查看结果(绿/红) 8. 失败则修复再运行 提示:单元测试跑在JVM上,无需模拟器,速度极快。推荐每次提交代码前,先跑一遍所有测试。

7. 写在最后的小总结

说实话,第一次成功跑通测试的感觉,真的挺爽的。你会发现代码变得可以验证了,不再靠猜。而且写完测试后,重构代码的胆子也大了不少——反正测试会帮你兜底。

如果你刚才跟着做了一遍,现在应该已经看到测试通过了。如果没通过,也别急,检查一下这几个地方:

  • 测试类是否放在 src/test/java/ 下?
  • build.gradle 里有没有 testImplementation 'junit:junit:4.13.2'?
  • 测试方法有没有加 @Test 注解?

大多数新手问题都出在这三个地方。我当初也是这么踩过来的,嗯,多练两次就习惯了。