unit-testing
28
总安装量
28
周安装量
#7295
全站排名
安装命令
npx skills add https://github.com/tencentblueking/bk-ci --skill unit-testing
Agent 安装分布
claude-code
18
opencode
16
codex
15
gemini-cli
15
cursor
13
antigravity
12
Skill 文档
åå æµè¯ç¼å
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 ææå¤é¨ä¾èµ
- è¦çæ£å¸¸åå¼å¸¸åºæ¯
- æµè¯æ¹æ³åæ¸ æ°æè¿°æµè¯æå¾