unittest

# 基于 cjc 使用(处理简单文件或包) cjc code.cj test.cj --test -o test_binary ./test_binary

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "unittest" with this command: npx skills add kong-baiming/cangjie-dev/kong-baiming-cangjie-dev-unittest

仓颉语言单元测试 Skill

  1. 快速入门

1.1 编译与运行

基于 cjc 使用(处理简单文件或包)

cjc code.cj test.cj --test -o test_binary ./test_binary

基于 cjpm 使用(标准项目)

cjpm test cjpm test src src/koo # 测试指定包

1.2 测试文件约定

  • 测试文件命名为 xxx_test.cj ,与 xxx.cj 并列

  • 测试模式下 unittest 和 unittest.testmacro 自动导入,无需显式 import

  1. 测试实体

2.1 测试函数

@Test func addTest() { @Expect(add(2, 3), 5) }

  • 顶层函数标记 @Test ,包含单个测试用例

2.2 测试类

@Test class AddTests { @TestCase func addPositive() { @Expect(add(2, 3), 5) }

@TestCase
func addZero() { @Expect(add(2, 0), 2) }

}

  • 类标记 @Test ,须有无参构造函数

  • 方法标记 @TestCase ,每个方法为一个测试用例

  1. 断言

3.1 断言宏列表

宏 失败行为 说明

@Assert(a, b)

快速失败:立即停止测试 相等性断言

@Expect(a, b)

软失败:记录失败,继续执行 相等性断言

@Assert(condition)

快速失败 布尔断言

@Expect(condition)

软失败 布尔断言

@AssertThrowsExType

快速失败 期望抛出异常

@ExpectThrowsExType

软失败 期望抛出异常

@Fail("msg")

强制失败,停止测试 强制标记失败

@FailExpect("msg")

强制失败,继续执行 强制标记失败

@PowerAssert(expr)

快速失败 详细中间值图示

  • @Assert(a, b) / @Expect(a, b) 要求 a 的类型实现 Equatable<B> (B 为 b 的类型)。若类型未实现 Equatable (如自定义枚举),须先通过 @Derive[Equatable] 或手动实现 Equatable 接口

3.2 近似相等(浮点数)

@Expect(1.0, 1.001, delta: 0.001)

  • 类型须实现 NearEquatable<CT, D> ,内置支持 Float16/32/64

  • 也支持 RelativeDelta 相对误差: @Expect(1.0 != 1.901, delta: RelativeDelta(absoluteDelta: 0.001, relativeDelta: 0.02))

  • 若 delta 参数不可用(取决于框架版本),可使用布尔断言替代: import std.math.* @Expect(abs(actual - expected) < 0.001)

3.3 自定义断言

@CustomAssertion public func checkNotNone<T>(ctx: AssertionCtx, value: ?T): T { match (value) { case Some(v) => v case None => ctx.fail("Expected non-None") } } // 使用:@AssertcheckNotNone

  1. 测试生命周期(仅限测试类)

宏 执行时机

@BeforeAll

所有测试用例执行前,执行一次

@BeforeEach

每个测试用例执行前

@AfterEach

每个测试用例执行后

@AfterAll

所有测试用例执行后,执行一次

执行顺序:BeforeAll → (BeforeEach → TestCase → AfterEach )* → AfterAll

  1. 参数化测试

5.1 值驱动

@Test[x in [1, 2, 3]] func squareTest(x: Int64) { @Expect(square(x), x * x) }

@Test[x in 0..10] func rangeTest(x: Int64) { ... }

5.2 随机参数

@Test[x in random()] func randomTest(x: Int64) { ... }

  • 生成约 200 个随机值,偏向边界值

5.3 多参数(笛卡尔积)

@Test[x in [1, 2], y in ["a", "b"]] func multiParamTest(x: Int64, y: String) { ... }

5.4 类型参数化

@Types[T in <Int64, Float64, String>] @Test func genericTest<T>() { ... }

5.5 可复用策略

@Strategy[param in source] func myStrategy(param: SomeType): ReturnType { ... }

@Test[data in myStrategy] func strategyTest(data: ReturnType) { ... }

  1. 基准测试

6.1 基本用法

@Test class MyBench { @Bench func sortBenchmark() { let arr = generateArray(10000) arr.sort() }

@Bench[size in [1000, 10000, 100000]]
func paramBench(size: Int64) {
    let arr = generateArray(size)
    arr.sort()
}

}

  • 使用 cjpm bench 运行

  • 框架自动处理预热、GC 噪声、统计分析(中位数、均值、置信区间)

6.2 测量源

@Measure[TimeNow(Nanos), CpuCycles()] @Bench func measuredBench() { ... }

  • 可用测量源:TimeNow 、CpuCycles 、Perf (Linux 性能计数器)
  1. 动态测试

@TestBuilder func dynamicTests(): TestSuite { TestSuite.builder("DynTests") .add(UnitTestCase.create("case1") { @Expect(1, 1) }) .add(UnitTestCase.create("case2") { @Expect(2, 2) }) .build() }

  • 编程方式构建测试套件
  1. 测试模板

@TestTemplate abstract class BaseTests { @BeforeEach func setup() { ... }

@TestCase
func commonTest() { ... }

}

@Test class MyTests <: BaseTests { @TestCase func specificTest() { ... } }

  • 定义可复用的测试用例和生命周期方法

  • 生命周期顺序:基类 @Before* 先执行,派生类 @After* 先执行

  1. 测试配置与标记

9.1 配置

@Configure[randomSeed: 42, generationSteps: 1000] @Test class ConfiguredTests { ... }

9.2 标签过滤

@Tag["fast"] @TestCase func fastTest() { ... }

命令行:cjpm test --include-tags "fast" --exclude-tags "slow"

9.3 跳过测试

@Skip @TestCase func skippedTest() { ... }

9.4 超时

@Timeout[Duration.second * 5] @TestCase func timeoutTest() { ... }

9.5 并行

@Parallel @Test class ParallelTests { ... }

  1. Mock 框架

10.1 创建 Mock/Spy 对象

let repo = mock<Repository>() // 骨架对象,无默认行为 let repo2 = mock<Repository>([ReturnsDefaults]) // 自动返回默认值 let spyObj = spy<Service>(realService) // 包装真实对象

  • mock:所有成员未实现 — 未配桩的调用抛出错误

  • spy:包装真实实例,未配桩的调用委托给原始实现

  • ReturnsDefaults :自动返回默认值(0、false、""、None、空集合)

  • SyntheticFields :将可变属性当作真实字段处理

10.2 配桩(@On 宏)

// 返回值 @On(repo.getData(testId, _)).returns(testResponse)

// 抛出异常 @On(repo.getData(_)).throws(TimeoutException())

// 调用失败 @On(repo.getData(_)).fails()

// 连续返回 @On(repo.getData(_)).returnsConsecutively([v1, v2, v3])

// 属性 getter/setter @On(foo.name).returns("test") @On(foo.name = _).doesNothing()

// 委托到真实实现(spy) @On(spyObj.method()).callsOriginal()

// 顶层/静态函数 @On(loadEntry()).returns(entry) @On(Entry.parse()).returns(testEntry)

10.3 参数匹配器

匹配器 说明 简写

any()

任意参数 _

eq(value)

结构相等 字面量/标识符

same(ref)

引用相等 —

ofType<T>()

类型检查 —

argThat(predicate)

自定义谓词 —

none()

Option None —

capture(listener)

捕获参数值 —

10.4 期望次数

@On(foo.bar()).returns().once() // 恰好 1 次 @On(foo.bar()).returns().times(3) // 恰好 3 次 @On(foo.bar()).returns().atLeastOnce() // 至少 1 次 @On(foo.bar()).returns().anyTimes() // 任意次数(不调用也不失败) @On(foo.bar()).returns().atLeastTimes(2) // 至少 2 次

10.5 桩链

@On(service.request()) .throws(TimeoutException()).times(2) .then().returns(response).once()

10.6 Synthetic Fields

let field = SyntheticField.create(initialValue: "initial") @On(foo.bar).getsField(field) @On(foo.bar = _).setsField(field)

10.7 参数捕获

let captor = ValueListener<String>.new() @On(renderer.renderBold(capture(captor))).callsOriginal() // 测试后: let args = captor.allValues() let last = captor.lastValue()

  1. 验证(Verify)

11.1 基本验证

Verify.that(@Called(foo.bar())) // 至少调用一次 Verify.that(@Called(foo.bar()).once()) // 恰好一次 Verify.that(@Called(foo.bar()).never()) // 从未调用 Verify.noInteractions(mockObj) // 无任何调用

11.2 有序验证

Verify.ordered( @Called(foo.step1()), @Called(foo.step2()), @Called(foo.step3()) )

11.3 无序验证

Verify.unordered( @Called(foo.bar(0)).times(2), @Called(foo.bar(1)).times(2) )

Verify.unordered(Partial, ...) // 忽略未列出的调用

11.4 动态验证

Verify.ordered { v => for (i in 0..n) { v.checkThat(@Called(foo.bar(i))) } }

11.5 清除调用日志

Verify.clearInvocationLog()

  1. CLI 选项

选项 说明

--bench

运行基准测试

--filter=pattern

过滤测试(通配符)

--dry-run

打印测试列表

--include-tags /--exclude-tags

按标签过滤

--show-tags

显示标签

--timeout-each=10s

单个测试超时

--parallel

并行执行

--report-path /--report-format

报告输出(xml/csv/html)

--no-capture-output

不捕获输出

--show-all-output

显示所有输出

--coverage-guided

覆盖率引导测试

  1. 关键类型参考

核心类

类 用途

TestSuite

构建和执行测试套件

UnitTestCase

创建/执行单个测试用例

Benchmark

创建/运行单个基准测试

ConsoleReporter / XmlReporter

输出报告器

Configuration

测试配置存储

Mock 相关类

类 用途

ConfigureMock

配置 mock 对象

Matchers

参数匹配器工厂

SyntheticField<T>

模拟可变字段

Verify

验证 API 入口

ValueListener<T>

参数捕获

关键接口

接口 用途

TestClass

提供 asTestSuite() 方法

NearEquatable<CT, D>

近似相等比较

Measurement

自定义基准测量

Arbitrary<T> / Shrink<T>

随机测试数据生成

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

inner_annotation

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

interface

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

enum

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

socket

No summary provided by upstream source.

Repository SourceNeeds Review