Kotlin的Hamcrest Matchers的先进创造

本文是对Java中较旧版本的重写。 这是在Kotlin中完成的。

上一次 ,我讨论了Hamcrest Matcher是什么,如何使用以及如何制作。 在本文中,我将解释创建Hamcrest Matchers的更多高级步骤。 首先,我将分享如何使您的匹配器更易于类型安全,然后介绍无状态匹配器的一些技术,最后是如何减少测试类上的大量静态导入。 我还将给您一些有关命名静态工厂方法的快速提示。

您可能已经在上次开发的matchs()方法中注意到了,我进行了类型检查。 潜在地,您还需要进行null检查,因为该方法接受Any? ,允许为空。 类型检查应该看起来很奇怪,因为我们从一个类中继承了一个类,该类具有一个我们指定为String的通用类型。

但是,如Hamcrest的文档中所述:
此方法与[匹配Any? ],而不是通用类型T 这是因为Matcher的调用者在运行时不知道类型是什么(因为使用Java泛型进行类型擦除)。

因此,我们需要确定要传入的对象的类型。此外,我们还应确保没有传入任何空值(除非我们的特定Matcher可以这样做,但这非常罕见),或者在至少要确保传入的null不会导致NullPointerException

但是有一种更简单的方法: TypeSafeMatcher 。 如果扩展此类而不是BaseMatcher类,它将为您执行类型检查和null检查,然后将对象传递给仅采用泛型指定类型的匹配方法。

定义TypeSafeMatcher是非常相似的定义匹配器我们上次做的方式,有一些不同:不是压倒一切matches()重写matchesSafely()这需要在通用型的,而不是Any? ; 而不是覆盖describeMismatch() ,而是覆盖describeMismatchSafely() 。 可能没有一个新的describeTo()可能令人惊讶,但是看到它除了Description之外没有任何其他内容,因此不需要类型安全的版本。

否则,创建TypeSafeMatcher是相同的。

不过,我不得不提及我上次忘记的事情。 定义自己的Matchers的人不需要重写describeMismatch()describeMismatchSafely()方法。 BaseMatcherTypeSafeMatcher都具有这些方法的默认实现,这些方法的简单实现是简单地输出“ was item.toString() ”(如果TypeSafeMatcher获得不正确类型的项,则“ TypeSafeMatcher of itemClassNameitem.toString()” )”。

这些默认实现通常足够好,但是如果使用的类型没有toString()的有用实现,则使用您自己的不匹配消息来描述项目的问题显然更有用。 即使该类具有不错的toString()实现,我也总是这样做,因为它可以更快地解决问题。

Hamcrest核心库中还有其他几个Matcher类,供用户从中扩展。 这些有几种口味。

首先,有CustomMatcherCustomTypeSafeMatcher 。 这些设计用于通过匿名类一次性创建Matchers。 它们可能很有用,但我希望始终进行适当的实现,以防万一我再次需要它。

接下来,有DiagnosingMatcherTypeSafeDiagnosingMatcher ,它们使您可以在matches()方法中创建不匹配描述。 这似乎是用一块石头杀死两只鸟的好方法,但是我有几块牛肉:1)它违反了SRP 2)如果存在不匹配,它再次调用matches()方法只是为了填充在不匹配说明中。 因此,第一个调用将忽略获取描述,第二个调用将忽略匹配。

您可以扩展的最后一个特殊Matcher是FeatureMatcher 。 这可能非常有用,但理解起来很复杂(我不确定我是否理解正确–直到我尝试自己动手做一个或阅读如何做一个为止)。 如果我弄清楚并获得了很好的理解,我将在这里为您写另一篇文章。
现在,让我们看一些可以与Matchers一起尝试的更高级的东西。

不需要将任何内容传递到其构造函数(因此,它是静态工厂方法)的任何Matcher都是无状态Matcher。 它们与其他Matcher相比有一个很小的优势,因为您只需要在任何时候存在一个实例,就可以在需要使用该Matcher的任何时间重用它。

这是一个非常简单的补充。 您需要做的就是创建该类的静态实例,并使您的静态工厂返回该实例,而不是调用构造函数。 库附带的IsEmptyString Matcher可以完成此操作(上一次我们的示例没有,但这是为了简单起见)。

用Hamcrest Matchers编写了相当多的测试之后,您可能会注意到文件顶部有很多导入。 一段时间后,这可能会成为很大的麻烦事,所以让我们看一下可以减轻此问题的方法。

实际上,这几乎与上一个解决方案一样简单。 您可以通过创建本质上为您完成的新文件来减少导入。 这个新类具有那些烦人的输入,但是随后定义了自己的静态工厂方法来委托给原始对象。 这对于将一堆类型的Matchers组合到一个导入中特别有用。 这是将一些核心Matchers组合到一个地方的示例:

 import org.hamcrest.core.IsEqual;  import org.hamcrest.core.IsNull;  import org.hamcrest.core.IsSame;  import org.hamcrest.Matcher;  fun isEqualTo(T object) = IsEqual.equalTo(object)  fun isNotANullValue() = IsNull.notNullValue()  fun isNotANullValue(Class type) = IsNull.notNullValue(type)  fun isANullValue() = IsNull.nullValue()  fun isANullValue(Class type) = IsNull.nullValue(type)  fun isTheSameInstanceAs(T target) = IsSame.sameInstance(target)  fun isTheInstance(T target) = IsSame.theInstance(target) 

定义此文件后,要使用任何或所有这些Matchers,只需导入module name .*还有一种生成这些组合Matcher类的方法,如官方Hamcrest教程所示 。 我不会继续讨论它,因为它不在本文讨论范围之内,而且我也不喜欢它。

如果您不喜欢这样导致额外的堆栈层,您可以始终使函数inline d。 在我看来,这是个理想的地方。

如果您阅读了官方的Hamcrest教程和/或查看了内置的Matchers,您可能会注意到静态工厂方法的命名趋势。 通用语法匹配“断言testObjectfactoryMethod ”。 方法名称的语法通常设计为当前时态动作,可以以“ is”开头。 在命名自己的静态工厂方法时,通常应遵循此约定,但实际上我建议您已经在名称中加上“ is”,正如您在上面的代码中可能已经注意到的那样。 这样,Matcher的用户无需将您的方法嵌套在is()方法内。 但是,如果执行此操作,则还需要创建反函数。 如果IsNull足以接受治疗,则没有理由不这样做。 允许使用is()方法包装Matcher的原因是,因此您也可以将其包装在not()方法中,以测试已经测试的内容的逆函数。 这导致了一个类似“断言testObject不是factoryMethod ”的句子。 但是,如果以这种方式定义两个函数,则在编写测试时不必担心所有这些毫无意义的括号。

如果您认为遵循约定对特定的Matcher而言过于严格,则只需确保您正在使用当前的时态动作测试即可。 例如,我做了一个匹配器,检查是否抛出了一个异常,该异常的静态工厂方法是throwsA() 。 我只是不喜欢将它命名为throwingA()以便与“ is”一起使用。 但是,如果再次违反约定,则必须确定要创建一个静态静态工厂方法。 例如, doesntThrowA() 。 如果要实现自己的逆工厂,最简单的方法通常是用not()包装正工厂。 因此,我的doesntThrowA()方法将返回not(throwsA()) 。 不过要小心:有时候,将正负误转实际上并不能给出您想要的正确逆。 例如,“检查集合中的所有项目均符合标准”的反面不是“没有项目符合标准”。

好吧,这就是我为您准备的。 如果您想让我进一步了解Hamcrest Matchers,请在评论中告诉我。 否则,您可以在其Hamcrest Matchers的github页面上进行自己的研究。 下次,我将讨论如何使Hamcrest Matchers以类似于AssertJ断言的流畅方式检查多个项目。

翻译自: https://www.javacodegeeks.com/2020/02/advanced-creation-of-hamcrest-matchers-in-kotlin.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值