mock 框架验证 API
验证 API 是 mock 框架的一部分,其功能如下:
- 验证是否进行了某些调用。
- 验证特定调用的次数。
- 验证是否使用特定参数进行调用。
- 验证调用是否按特定顺序进行。
验证通过检查在执行测试期间构建的调用日志来运行断言。调用日志涵盖让 mock 和 spy 对象在测试中可访问的所有调用。只能验证在 mock/spy 对象上进行的调用。
Verify
类是验证 API 的入口。
@Called 宏用于构建关于代码的断言。
@Called 宏调用构造了一个 验证语句 ,即根据调用日志检查代码的单个断言。
Verify 类本身是静态方法的集合。诸如 that
、 ordered
、 unordered
等方法可构造验证块。
示例
let foo = mock<Foo>()
//配置foo
@On(foo.bar()).returns()
foo.bar()
Verify.that(@Called(foo.bar())) //验证bar至少被调用一次
验证语句和 @Called
宏
验证语句由 VerifyStatement
类表示。 VerifyStatement
实例由 @Called
宏创建。
@Called
宏调用接受桩签名,类似于 @On
宏,并且适用参数匹配器的规则。
示例:
@Called(foo.bar(1, _)) //匹配bar方法调用的验证语句,其中第一个参数为'1'
@Called(foo.baz) //匹配baz属性getter调用的验证语句
VerifyStatement
类提供的 API 类似于桩配置时可用的基数说明符。
基数函数为:
once()
atLeastOnce()
times(expectedTimes: Int64)
times(min!: Int64, max!: Int64)
atLeastTimes(minTimesExpected: Int64)
never()
调用这些函数会返回相同的 VerifyStatement
实例。同一语句不能重置基数,且必须在语句传递到验证块生成器函数之前设置基数。如果没有显式设置基数,则使用默认基数。
Verify.that(@Called(foo.bar()).atLeastOnce())
Verify.that(@Called(foo.bar()).once())
验证块
验证块通常包含一个或多个验证语句,且检查块中的语句会构成更复杂的断言。
在调用验证块时会立即验证,不验证之后发生的任何调用。 验证块不会改变调用日志的状态:日志中的每个调用都可以被任意数量的块检查。独立检查各个块,前后块之间没有依赖关系。除非在块之间发生了一些调用,或者手动清除了调用日志,否则调用验证块的顺序并不重要。
验证语句本身不执行任何类型的验证,必须传递到验证块中进行验证。
单个验证块仅检查在块内验证语句中提到的 mock/spy 对象上的调用,忽略对其他对象的调用。
Verify
类包含几个构建验证块的静态方法。有序验证块用于检查调用的确切顺序。无序验证块只验证调用的次数。
有序
如需检查一个或多个对象的调用顺序,使用 ordered
验证块生成器。
ordered
静态函数接收一个验证语句数组。
for (i in 0..4) {
foo.bar(i % 2)
}
Verify.ordered(
@Called(foo.bar(0)),
@Called(foo.bar(1)),
@Called(foo.bar(0)),
@Called(foo.bar(1))
)
允许检查多个对象的调用顺序。
for (i in 0..4) {
if (i % 2 == 0) {
fooEven.bar(i)
}
else {
forOdd.bar(i)
}
}
Verify.ordered(
@Called(fooEven.bar(0)),
@Called(fooOdd.bar(1)),
@Called(fooEven.bar(2)),
@Called(fooOdd.bar(3)),
)
有序验证的默认基数说明符是 once()
。如有需要,可使用其他基数说明符。
for (i in 0..4) {
foo1.bar(i)
}
for (i in 0..4) {
foo2.bar(i)
}
Verify.ordered(
@Called(foo1.bar(_).times(4)),
@Called(foo2.bar(_).times(4))
)
对于有序验证,须列出对(块中提到的) mock/spy 对象的所有调用。任何未列出的调用都会导致验证失败。
foo.bar(0)
foo.bar(10)
foo.bar(1000)
Verify.ordered(
@Called(foo.bar(0)),
@Called(foo.bar(10))
)
输出:
验证失败
以下调用未匹配到任何语句:
foo.bar(...) at example_test.cj:6
无序
无序验证只检查其验证语句的调用次数。
对于无序验证,除非显式指定,否则使用 atLeastOnce()
基数,即检查至少进行了一次的调用。
for (i in 0..4) {
foo.bar(i % 2)
}
//验证是否至少调用了一次使用参数0和参数1的bar
Verify.unordered(
@Called(foo.bar(0)),
@Called(foo.bar(1))
)
//验证是否调用了两次使用参数0和参数1的bar
Verify.unordered(
@Called(foo.bar(0)).times(2),
@Called(foo.bar(1)).times(2)
)
//验证是否总共调用了四次bar
Verify.unordered(
@Called(foo.bar(_)).times(4)
)
无序验证包括 Partial 和 Exhaustive 。
默认为 Exhaustive ,需要列出验证语句所提到的 mock/spy 对象的所有调用。 Partial 只列出部分调用。
for (i in 0..4) {
foo.bar(i)
}
//失败,foo.bar()的两次调用未在块中列出
Verify.unordered(
@Called(foo.bar(0)).once(),
@Called(foo.bar(1)).once()
)
//忽略无关调用
Verify.unordered(Partial,
@Called(foo.bar(0)).once(),
@Called(foo.bar(1)).once()
)
动态构建验证块
ordered
和 unordered
函数为接受 lambda 的重载函数。可使用 checkThat(statement: VerifyStatement)
函数动态添加语句。
示例:
let totalTimes = 40
for (i in 0..totalTimes) {
foo.bar(i % 2)
}
Verify.ordered { v =>
for (j in 0..totalTimes) {
v.checkThat(@Called(foo.bar(eq(j % 2))))
}
}
其他 API
另外,Verify
类还提供了以下工具。
- that(statement: VerifyStatement) 为 Verify.unordered(Paritial, statement) 的别名,用于检查单个语句,不需要列出对应 mock/spy 对象的所有调用。
- noInteractions(mocks: Array