
即使被调用服务(如 ServiceB)已有完备测试,调用方(如 ServiceA)仍需针对其对依赖行为的响应逻辑进行单元测试——重点验证真值、假值及异常三种场景下的流程完整性与协作正确性。
即使被调用服务(如 serviceb)已有完备测试,调用方(如 servicea)仍需针对其**对依赖行为的响应逻辑**进行单元测试——重点验证真值、假值及异常三种场景下的流程完整性与协作正确性。
在微服务或分层架构中,ServiceA 调用 ServiceB.shouldVerify(...) 并仅透传其返回的布尔值,看似“无逻辑”,但这种调用本身构成了 ServiceA 的关键协作契约。单元测试的目标不是重复验证 ServiceB 的内部逻辑,而是保障 ServiceA 在面对 ServiceB 各种可能响应时的行为符合预期。 忽略此类测试,将导致集成风险前移、故障定位困难,且违背“测试应覆盖被测类所有可观测行为”的基本原则。
因此,ServiceA.foo() 的单元测试必须覆盖以下三类场景:
- ✅ serviceB.shouldVerify() 返回 true:验证 ServiceA 后续逻辑(如参数传递、状态更新、调用下游服务等)是否按预期执行;
- ✅ 返回 false:验证分支路径是否被正确处理(例如跳过某操作、设置默认值等);
- ✅ 抛出异常(如 RuntimeException 或自定义业务异常):验证 ServiceA 是否具备合理的异常传播、降级或日志记录机制。
示例(使用 Mockito + JUnit 5):
@ExtendWith(MockitoExtension.class)
class ServiceATest {
@Mock
private ServiceB serviceB;
@InjectMocks
private ServiceA serviceA;
@Test
void foo_shouldProceedWhenShouldVerifyReturnsTrue() {
// given
when(serviceB.shouldVerify(any())).thenReturn(true);
// when
serviceA.foo();
// then: 验证下游服务被正确调用(假设后续调用 serviceC.process(...))
verify(serviceC).process(true);
}
@Test
void foo_shouldHandleFalseResponse() {
// given
when(serviceB.shouldVerify(any())).thenReturn(false);
// when
serviceA.foo();
// then: 验证未触发不应发生的操作
verify(serviceC, never()).process(true);
// 或验证执行了 fallback 逻辑
verify(logger).warn("Verification skipped");
}
@Test
void foo_shouldPropagateExceptionFromServiceB() {
// given
when(serviceB.shouldVerify(any()))
.thenThrow(new RuntimeException("Remote check failed"));
// when & then
assertThrows(RuntimeException.class, () -> serviceA.foo());
// 同时可验证异常日志是否记录
verify(logger).error(eq("Failed to verify via ServiceB"), any(RuntimeException.class));
}
}⚠️ 关键注意事项:
- 不测试 ServiceB 的实现细节:ServiceB 的单元测试已覆盖其 shouldVerify 的业务逻辑(如规则校验、数据库查询等),ServiceA 测试中只需 模拟 其契约化输出;
- 避免“空测试”陷阱:仅验证 serviceB.shouldVerify() 被调用一次(verify(serviceB).shouldVerify(...))是不够的——这属于“测试替身行为”,而非被测类行为,无法保证业务流程正确性;
- 异常类型需与实际一致:若 ServiceB 声明抛出受检异常(如 IOException),测试中需相应 when(...).thenThrow(...) 并验证 ServiceA 的异常处理策略(如 try-catch、转换为运行时异常等);
- 真实依赖隔离:务必通过依赖注入(如 Spring @MockBean 或手动构造)确保 ServiceB 被有效 mock,防止测试意外触发远程调用或数据库操作。
总结而言,对 ServiceA 中服务调用的测试,本质是契约驱动的协作测试(Collaboration Test):它不关心 ServiceB 怎么算出 true/false,而专注 ServiceA 如何可靠地消费这个结果。这种测试粒度精准、反馈快速、维护成本低,是构建高可信度服务链路的基石。










