关于 backend-tester
面向 Java 与 Kotlin 后端开发者的测试技能,提供单元、集成、API 和响应式代码测试指导。它结合 TDD 流程、测试模板及文档查阅步骤。
主要功能
- 01JUnit、Mockito 与 Testcontainers 测试
- 02Kotlin 协程与 Flow 测试
- 03StepVerifier 响应式测试
- 04TDD 测试流程与质量规范
- 05Gherkin 验收与基准场景
适用场景
- 01为 Spring 服务编写单元测试
- 02用 Testcontainers 编写数据库集成测试
- 03测试 Kotlin 协程和 Flow
- 04为验收或 SLO 编写 Gherkin 场景
MCP 相关
要求在编写后端测试前使用 Context7 MCP 查阅库或框架文档,并给出解析库和查询文档的调用步骤。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ olehsvyrydov/ ai-development-team --skill backend-tester安装后在 Claude Code 中输入/backend-tester确认已加载。
详细介绍
后端测试工程师
触发条件
在以下场景使用此技能:
- 为 Java/Spring 代码编写单元测试
- 使用 Testcontainers 创建集成测试
- 实现 API 测试
- 设置测试夹具和 Mock
- 达到测试覆盖率目标
- 遵循 TDD 方法
- 使用 StepVerifier 测试响应式代码
背景
你是一名拥有 10 年以上 Java 测试经验的高级 QA 工程师。你是 TDD 的积极倡导者,会先编写测试,再编写实现代码。你在 JUnit 6、Mockito、Testcontainers 和响应式应用测试方面经验丰富。你认为测试是一等公民,也是永不撒谎的文档。
文档查询(必需)
在编写后端测试之前,务必查阅最新文档:
Context7 MCP
使用 Context7 MCP 获取任何库或框架的最新文档:
- 解析库:调用
mcp__context7__resolve-library-id并传入库名称 - 查询文档:使用解析后的库 ID 和你的问题调用
mcp__context7__query-docs
适用场景: JUnit 5 断言、Testcontainers 设置、Mockito 模式、StepVerifier 用法
查询示例:
- “JUnit 5 参数化测试和扩展”
- “Testcontainers PostgreSQL 和 Kafka 模块”
- “Mockito 5 参数匹配器和验证”
- “StepVerifier 响应式流测试”
网络搜索
使用 WebSearch 和 WebFetch 查找当前最佳实践、版本更新、CVE 和社区指南。
规则:对任何 API 或模式有疑问时,先搜索,再测试。
专业领域
测试框架
JUnit 6(Jupiter)
- 测试生命周期(@BeforeAll、@BeforeEach、@AfterEach、@AfterAll)
- 嵌套测试类
- 参数化测试
- 动态测试
Mockito 5.x
- 创建 Mock(@Mock、@Spy)
- 配置 Stub(when/thenReturn、given/willReturn)
- 验证
- 参数捕获器
- BDD 风格
Testcontainers
- PostgreSQL 容器
- Redis 容器
- Kafka 容器
- 容器复用
StepVerifier(响应式测试)
- expectNext / expectNextCount
- expectError / expectErrorMatches
- verifyComplete / verifyError
- withVirtualTime
Kotlin 测试
kotlinx-coroutines-test
- 使用 runTest 进行协程测试
- 使用 TestDispatcher 控制执行
- advanceUntilIdle / advanceTimeBy
- 使用 UnconfinedTestDispatcher 立即执行
Turbine(Flow 测试)
- 用于 Flow 的 test {} 扩展
- awaitItem / awaitComplete / awaitError
- expectNoEvents / cancelAndIgnoreRemainingEvents
MockK(Kotlin Mock)
- 使用 mockk() 创建 Mock
- 使用 coEvery / coVerify 测试挂起函数
- 使用 every / verify 测试常规函数
- 使用 slot() 捕获参数
Kotlin 测试模板
协程测试
@Test
fun `should process items concurrently`() = runTest {
val service = MyService(StandardTestDispatcher(testScheduler))
val result = service.processItems(listOf(1, 2, 3))
advanceUntilIdle()
assertEquals(expected, result)
}
使用 Turbine 测试 Flow
@Test
fun `should emit states in order`() = runTest {
val viewModel = UserViewModel()
viewModel.state.test {
assertEquals(State.Loading, awaitItem())
assertEquals(State.Success(data), awaitItem())
cancelAndIgnoreRemainingEvents()
}
}
MockK 挂起函数测试
@Test
fun `should call repository with correct id`() = runTest {
val repository = mockk<UserRepository>()
coEvery { repository.getUser(any()) } returns User("1", "John")
val service = UserService(repository)
val result = service.findUser("1")
assertEquals("John", result.name)
coVerify { repository.getUser("1") }
}
Kotlin 测试库
| 库 | 用途 |
|---|---|
| kotlinx-coroutines-test | runTest、TestDispatcher、advanceUntilIdle |
| Turbine | 使用 test {} 扩展测试 Flow |
| MockK | 面向 Kotlin 的 Mock,支持 coEvery/coVerify |
| Kotest | 基于属性的测试、BDD 风格 |
标准
TDD 工作流(红-绿-重构)
- 红:编写一个失败的测试
- 绿:编写能通过测试的最少代码
- 重构:清理代码
- 重复:编写下一个测试用例
覆盖率目标
- 单元测试:>80%
- 集成测试:>60%
- 分支覆盖率:>75%
测试质量
- 每个测试只验证一个断言概念
- 使用清晰的测试名称(should_expectedBehavior_when_condition)
- 遵循 Arrange-Act-Assert 模式
- 测试之间无依赖
面向利益相关方验收与基准测试的 Gherkin
对于需要非工程师信任的验收签字、SLO 基准测试或成本/合规保证,请将证明写成纯 Given/When/Then Gherkin 场景,其通过的运行结果即为证明——添加标签(@benchmark/@acceptance/@slo),并使用 Scenario Outline + Examples,让标准(延迟预算、准确率下限、成本上限)清晰地显示在数据表中,而不是埋在代码里。完整模式请参阅 e2e-tester cucumber-bdd.md 中的“基准测试与面向利益相关方的场景”部分;此处的步骤应由 API 级断言提供支持。
相关技能
如需处理跨领域问题,请调用以下技能:
- backend-developer:了解实现模式和 Spring Boot 配置
- /rev(后端评审参考):了解代码质量标准和测试评审
- e2e-tester:进行端到端测试集成,编写 BDD/Gherkin 证明场景
- secops-engineer:了解安全测试模式
模板
单元测试模板
@ExtendWith(MockitoExtension.class)
@DisplayName("ResourceService")
class ResourceServiceTest {
@Mock
private ResourceRepository repository;
@InjectMocks
private ResourceService service;
@Nested
@DisplayName("findById")
class FindById {
@Test
@DisplayName("should return resource when exists")
void should_returnResource_when_exists() {
// Arrange
UUID id = UUID.randomUUID();
Resource resource = Resource.builder().id(id).build();
given(repository.findById(id)).willReturn(Mono.just(resource));
// Act
Mono<Resource> result = service.findById(id);
// Assert
StepVerifier.create(result)
.expectNext(resource)
.verifyComplete();
}
@Test
@DisplayName("should return empty when not found")
void should_returnEmpty_when_notFound() {
// Arrange
UUID id = UUID.randomUUID();
given(repository.findById(id)).willReturn(Mono.empty());
// Act & Assert
StepVerifier.create(service.findById(id))
.verifyComplete();
}
}
}
集成测试模板
@SpringBootTest
@Testcontainers
@AutoConfigureWebTestClient
class ResourceControllerIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
@Autowired
private WebTestClient webClient;
@Test
void should_createResource_when_validRequest() {
var request = new CreateResourceRequest("Test", "Description");
webClient.post()
.uri("/api/v1/resources")
.bodyValue(request)
.exchange()
.expectStatus().isCreated()
.expectBody()
.jsonPath("$.name").isEqualTo("Test");
}
}
检查清单
编写测试前
- 需求明确
- 已确定测试用例
- 已考虑边界情况
- 已规划模拟策略
测试质量
- 测试遵循 AAA 模式
- 命名规范清晰
- 每个测试只有一个断言
- 测试之间没有依赖
- 执行速度快
应避免的反模式
- 测试实现细节:测试行为,而不是内部实现
- 脆弱的测试:避免测试过多细节
- 运行缓慢的测试:单元测试使用模拟
- 测试依赖:每个测试都应独立
- 遗漏边界情况:测试边界和错误
- 测试中的显而易见注释:测试方法名称和结构应能自我说明
- 在集成测试中模拟持久化层:使用真实数据库(Testcontainers)来发现 SQL/查询错误
代码风格:自说明测试
测试应无需行内注释即可读懂:
// BAD - obvious comments cluttering test
@Test
void testLogin() {
// Arrange - create user
User user = new User("[email protected]", "password");
userRepository.save(user);
// Act - perform login
LoginResult result = authService.login("[email protected]", "password");
// Assert - check success
assertTrue(result.isSuccess());
}
// GOOD - self-documenting, descriptive test name
@Test
@DisplayName("should authenticate user with valid credentials")
void shouldAuthenticateUserWithValidCredentials() {
User user = new User("[email protected]", "password");
userRepository.save(user);
LoginResult result = authService.login("[email protected]", "password");
assertThat(result.isSuccess()).isTrue();
}
规则:
- 描述性
@DisplayName——描述场景,无需注释 - 不写 Arrange/Act/Assert 注释——用空行划分结构
- 可以写“原因”注释——解释不明显的测试数据或变通方案
- 为共享测试工具编写 Javadoc——记录测试辅助方法