graph LR
A[”Arrange<br/>准备阶段”] --> B[”Action<br/>执行阶段”] --> C[”Assert<br/>断言阶段”]
A -.-> A1[”创建 Stub/Mock<br/>设置预期行为<br/>准备输入数据”]
B -.-> B1[”调用被测方法<br/>驱动目标代码”]
C -.-> C1[”验证返回值<br/>verify 交互<br/>检查状态变更”]
每个阶段的职责
阶段
英文
职责
典型操作
准备
Arrange
搭建测试场景
new 对象、mock() 创建替身、when(...).thenReturn(...) 设置预期
执行
Action
驱动被测代码
调用目标方法、传入参数、捕获返回值
断言
Assert
验证行为正确性
assertEquals、verify()、assertThrows、andExpect
完整示例
@Testvoid calculatePrice_givenTotal90_willAddExpressFee20() { // ========== Arrange — 准备阶段 ========== var calculator = new PriceCalculator(); var expressServiceMock = mock(ExpressService.class); when(expressServiceMock.getTodayPrice()).thenReturn(20); calculator.setExpressService(expressServiceMock); // ========== Action — 执行阶段 ========== var result = calculator.calculatePrice(70, 20); // total = 90 // ========== Assert — 断言阶段 ========== assertEquals(110, result); // 验证返回值 verify(expressServiceMock).getTodayPrice(); // 验证交互}
反例:缺少 AAA 结构
// ❌ 坏味道:三个阶段混在一起@Testvoid test() { var calc = new PriceCalculator(); // Arrange var result = calc.calculatePrice(70, 20); // Action when(expressService.getTodayPrice()).thenReturn(20); // Arrange 放在了 Action 后面! assertEquals(result, 110); // Assert 参数顺序错误(expected, actual)}
问题: 准备代码放在执行之后,测试逻辑混乱,不易阅读。
AAA 的灵活运用:异常场景
@Testvoid signIn_givenInvalidPassword_willThrowException() { // Arrange var authService = new AuthService(); var userRepoMock = mock(UserRepository.class); when(userRepoMock.findByUsername("test")).thenReturn(null); authService.setUserRepository(userRepoMock); // Action & Assert(结合) assertThrows(UsernameNotFoundException.class, () -> { authService.signIn("test", "wrongPassword"); });}