假设我在Kotlin中有以下数据结构(从实际需要简化):
data class RootClass(
val a: String,
val nestedContent: MiddleClass,
) : Result
data class MiddleClass(
val foo: String,
val leaf: Leaf,
)
data class Leaf(
val value: String,
val leaf: Leaf?,
)
字符串
为了在测试中进行Assert/验证,是否有某种方法(例如使用某个库)以声明的方式定义预期结果,例如允许忽略任意字段。
RootClass(
a = "Foo",
nestedContent = MiddleClass(
foo = anyString(),
leaf = Leaf(
value = "leaf-value",
leaf = any(Leaf)
)
)
)
型
我研究了一下assertK、Hamcrest和mockK匹配器等,还不是很深入,但还没有找到“最佳方法”。对于典型的用例,通常可以创建自定义帮助程序来检查想要的字段,但对上面的东西进行测试偶尔会很有用,而且容易阅读。
更新:对于“通过示例说明”的用法,“照原样”获得预期结果是有用的。
2条答案
按热度按时间ua4mk5z41#
如果你想声明,type safe builders模式非常有用。
首先,声明一个表示匹配器的接口。
字符串
然后你就可以创建各种各样的实现了(例如Hamcrest中提供的各种匹配器)。现在,我只需要这些简单的:
型
然后我们可以编写一个构建器,它构建了一种新的匹配器,只有当它包含的
Matcher<T>
列表都匹配时才匹配。型
这样,我们就可以创建一个
Matcher<RootClass>
,就像你的问题中的那样:型
通过对名称进行一些调整,您可以使其更具可读性。
作为扩展,我在这里添加了一个
AnyMatcher
:型
uklbhaso2#
另一种表示
Matcher<T>
的方法是一个函数,它接受T
,并以某种方式更新MatcherContext
。字符串
请注意,我使用了
Match<T>
类型的上下文接收器和常规接收器,以使use-site尽可能具有声明性。use site不需要一直传递MatcherContext
。现在
anyNonNull
匹配器可以写成:型
为了在结构上匹配像
RootClass
这样的类型,我们的想法是将Matcher<T>
写为lambda。在lambda内部,我们将编写最终调用MatcherContext.update
的语句。这些语句将调用像这样的方法:型
我特意将其命名为
=
,以使使用站点看起来更好。=
函数(以及其他接受上下文接收器的函数)不幸的是现在不能是inline
,因为this compiler bug。现在我们可以在问题中编写匹配器:
型
请注意,每个
=
调用都会更新当前上下文。=
之后的每个{ ... }
都会打开一个新的AND上下文。返回
Boolean
的实际match
函数也很简单-打开一个上下文,将匹配器应用于上下文,然后返回其matched
属性。型
与my other answer相比,它不需要到处引用属性,但它并不懒惰。例如,在AND上下文中,即使它找到不匹配的东西,它仍然会尝试评估其余的匹配器。
现在是时候做一些扩展了:
型