unit-testing
Installation
SKILL.md
单元测试编写
Quick Reference
框架:JUnit 5 (Jupiter) + MockK 1.12.2
测试基类:BkCiAbstractTest(提供 dslContext、objectMapper)
文件命名:*Test.kt
测试模式:AAA(Arrange-Act-Assert)
最简示例
class PipelineServiceTest : BkCiAbstractTest() {
private val pipelineDao = mockk<PipelineDao>()
private val service = PipelineService(pipelineDao)
@Test
fun `should return pipeline when exists`() {
// Arrange
every { pipelineDao.get(any(), any()) } returns mockPipeline
// Act
val result = service.getPipeline(PROJECT_ID, PIPELINE_ID)
// Assert
Assertions.assertNotNull(result)
verify { pipelineDao.get(PROJECT_ID, PIPELINE_ID) }
}
companion object {
const val PROJECT_ID = "test-project"
const val PIPELINE_ID = "p-12345678901234567890123456789012"
}
}
When to Use
- 编写 Service/DAO 层单元测试
- Mock 外部依赖
- 验证业务逻辑正确性
- 进行 TDD 开发
When NOT to Use
- 集成测试 → 需要启动完整服务
- E2E 测试 → 需要部署完整环境
测试基类
abstract class BkCiAbstractTest {
protected val dslContext: DSLContext = DSL.using(
MockConnection(Mock.of(0)),
SQLDialect.MYSQL
)
protected val objectMapper: ObjectMapper = JsonUtil.getObjectMapper()
}
Mock 创建方式
// 基础 Mock
private val dao = mockk<PipelineDao>()
// Relaxed Mock(自动返回默认值)
private val service = mockk<PipelineService>(relaxed = true)
// Spy(部分 Mock)
private val self = spyk(MyService(), recordPrivateCalls = true)
// Spring Bean Mock
mockkObject(SpringContextUtil)
every { SpringContextUtil.getBean(CommonConfig::class.java) } returns config
Stub 行为定义
// 简单返回
every { dao.get(any(), any()) } returns mockData
// 条件应答
every { redis.execute(any<RedisScript<*>>(), any(), any()) } answers {
val script = args[0] as DefaultRedisScript<*>
if (script.resultType == Long::class.java) 1L else throw RuntimeException()
}
// 抛出异常
every { service.doSomething() } throws ErrorCodeException(...)
断言与验证
// 基本断言
Assertions.assertEquals(expected, actual)
Assertions.assertTrue(condition)
Assertions.assertNull(value)
// 异常断言
val ex = assertThrows<ErrorCodeException> { service.doSomething() }
Assertions.assertEquals("2100013", ex.errorCode)
// 验证调用
verify { dao.get(any(), any()) }
verify(exactly = 1) { service.save(any()) }
verify(exactly = 0) { service.delete(any()) }
测试组织
class MyServiceTest {
@Nested
inner class GetPipelineTests {
@Test
@DisplayName("流水线存在时返回数据")
fun `returns pipeline when exists`() { }
@Test
@DisplayName("流水线不存在时抛出异常")
fun `throws exception when not found`() { }
}
}
测试数据构建
// Builder 模式
fun buildOptions(
enable: Boolean = true,
runCondition: RunCondition = RunCondition.PRE_TASK_SUCCESS
) = ElementAdditionalOptions(enable = enable, runCondition = runCondition)
// 从资源文件加载
val resource = ClassPathResource("test-data/pipeline.json")
val data = JsonUtil.to(resource.inputStream, PipelineInfo::class.java)
Checklist
编写测试前确认:
- 继承
BkCiAbstractTest基类 - 使用 AAA 模式组织测试代码
- Mock 所有外部依赖
- 覆盖正常和异常场景
- 测试方法名清晰描述测试意图
Related skills
More from tencentblueking/bk-ci
frontend-vue-development
前端 Vue 开发规范,涵盖 Vue 2/3 组件开发、Vuex 状态管理、路由配置、组件通信、样式规范、国际化。当用户进行前端开发、编写 Vue 组件、处理状态管理或实现页面交互时使用。
146git-commit-specification
Git 提交规范,涵盖 commit message 格式(feat/fix/refactor)、Issue 关联、分支命名、PR 提交准备、rebase 使用。当用户提交代码、编写 commit message、创建分支或准备 PR 时使用。
63design-patterns
BK-CI 项目设计模式实践指南,涵盖工厂模式、策略模式、观察者模式、装饰器模式、模板方法等在项目中的实际应用。当用户学习设计模式、重构代码、设计可扩展架构或理解项目设计时使用。
62backend-microservice-development
后端微服务开发规范,涵盖目录结构、分层架构(API/Service/DAO)、依赖注入、配置管理、Spring Boot 最佳实践。当用户进行后端开发、创建新微服务、编写 Kotlin/Java 代码或设计服务架构时使用。
60database-design
BK-CI 数据库设计规范与表结构指南,涵盖命名规范、字段类型选择、索引设计、分表策略、数据归档。当用户设计数据库表、优化索引、规划分表策略或进行数据库架构设计时使用。
53api-interface-design
API 接口设计规范,涵盖 RESTful 设计原则、URL 命名、HTTP 方法选择、请求响应格式、错误码定义、版本控制。当用户设计 API 接口、定义 Resource 类、编写接口文档或进行接口评审时使用。
51