Elastic4s,mockito 和类型擦除和隐式验证

Elastic4s, mockito and verify with type erasure and implicits

在以前的 Elastic4s 版本中,你可以做类似的事情

val argument1: ArgumentCapture[DeleteIndexDefinition] = ???
verify(client).execute(argument1.capture())
assert(argument1 == ???)

val argument2: ArgumentCapture[IndexDefinition] = ???
verify(client, times(2)).execute(argument2.capture())
assert(argument2 == ???)

在你的测试中执行了几次之后(即一次 DeleteIndexDefinition,接着是两次 IndexDefinition)。每个验证都将与其类型匹配。

但是,Elastic4s 现在在其 client.execute 方法中采用隐式参数。该参数的类型为 Executable[T,R],这意味着您现在需要

val argument1: ArgumentCapture[DeleteIndexDefinition] = ???
verify(client).execute(argument1.capture())(any[Executable[DeleteIndexDefinition,R]])
assert(argument1 == ???)

val argument2: ArgumentCapture[IndexDefinition] = ???
verify(client, times(2)).execute(argument2.capture())(any[Executable[IndexDefinition,R]])
assert(argument2 == ???)

这样做之后,我收到了一个错误。 Mockito 在第一次验证时考虑了这三个 client.execute。是的,即使第一个参数是不同的类型。

这是因为隐式(第二个参数)在类型擦除后具有相同的类型Executable

所以断言失败了。如何在此设置中进行测试?

我的解决方案是创建一个通用类型的验证。我花了一段时间才意识到,即使没有通用类型,你也总是有 AnyRef。

所以,像这样的东西行得通

val objs: ArgumentCaptor[AnyRef] = ArgumentCaptor.forClass(classOf[AnyRef])
verify(client, times(3)).execute(objs.capture())(any())
val values = objs.getAllValues
assert(values.get(0).isInstanceOf[DeleteIndexDefinition])
assert(values.get(1).isInstanceOf[IndexDefinition])
assert(values.get(2).isInstanceOf[IndexDefinition])

我已经创建了问题和答案。但我会考虑其他答案。

elastic4s 现在采用的封装执行每种请求类型的逻辑的方法是使用typeclasses。这就是隐式现在存在的原因。它有助于模块化每个请求类型,并避免开始潜入 ElasticClient class.

的 God class 反模式

我能想到的两件事可能会对您有所帮助:

  1. 您已经发布的内容,使用 Mockito 并将隐式作为另一个匹配器传递。这就是一般使用隐式模拟方法的方法。

  2. 不使用 mockito,而是假脱机一个本地嵌入式节点,并针对真实数据进行尝试。这是我编写 elasticsearch 代码时的首选方法。优点是您正在针对真实服务器测试真实查询,因此不仅要检查它们是否被调用,还要检查它们是否确实有效。 (有些人可能认为这是一个集成测试,但无论我不同意什么,它都在一个独立的测试中运行,没有外部部门)。

elastic4s 的最新版本甚至包含一个 testkit,这使得获取嵌入式节点变得非常容易。您几乎可以查看任何单元测试,了解如何使用它。