我试图用一个包含lambda函数的方法对一个Java类进行单元测试。我使用Groovy和Spock进行测试。出于专有原因,我不能显示原始代码。
Java方法如下所示:
class ExampleClass {
AsyncHandler asynHandler;
Component componet;
Component getComponent() {
return component;
}
void exampleMethod(String input) {
byte[] data = input.getBytes();
getComponent().doCall(builder ->
builder
.setName(name)
.data(data)
.build()).whenCompleteAsync(asyncHandler);
}
}
字符串
其中component#doCall
具有以下签名:
CompletableFuture<Response> doCall(Consumer<Request> request) {
// do some stuff
}
型
Groovy测试如下所示:
class Spec extends Specification {
def mockComponent = Mock(Component)
@Subject
def sut = new TestableExampleClass(mockComponent)
def 'a test'() {
when:
sut.exampleMethod('teststring')
then:
1 * componentMock.doCall(_ as Consumer<Request>) >> { args ->
assert args[0].args$2.asUtf8String() == 'teststring'
return new CompletableFuture()
}
}
class TestableExampleClass extends ExampleClass {
def component
TestableExampleClass(Component component) {
this.component = component;
}
@Override
getComponent() {
return component
}
}
}
型
如果我在assert
行上放置断点,捕获的参数args
将在调试窗口中显示如下:
args = {Arrays$ArrayList@1234} size = 1
> 0 = {Component$lambda}
> args$1 = {TestableExampleClass}
> args$2 = {bytes[]}
型
有两点令我困惑:
1.当我试图将捕获的参数args[0]
转换为ExampleClass
或TestableExampleClass
时,它抛出了一个GroovyCastException
。我相信这是因为它需要Component$Lambda
,但我不确定如何转换。
1.使用args[0].args$2
访问data
属性,似乎不是一种简单的方法。这可能与上面提到的强制转换问题有关。但是,是否有更好的方法来实现这一点,例如使用args[0].data
?
即使不能给出直接的答案,指向一些文档或文章的指针也会很有帮助。我的搜索结果分别讨论了Groovy闭包和Java lambdas比较,但没有讨论在闭包中使用lambdas。
1条答案
按热度按时间tkclm6bt1#
为什么你不应该做你正在尝试的事情
这种入侵式的测试简直是一场噩梦!很抱歉我的措辞过于强硬,但我想明确一点,你不应该像这样过度指定测试,Assertlambda表达式的私有final字段。为什么lambda表达式中的内容很重要呢?只需验证结果。为了进行这样的验证,你需要
1.需要了解Java中如何实现Java的内部原理,
1.这些实现细节必须在Java版本中保持不变,
1.这些实现甚至必须在JVM类型(如Oracle Hotspot,OpenJ 9等)之间保持相同。
否则,你的测试很快就会失败。你为什么要关心一个方法内部如何计算它的结果呢?一个方法应该像一个黑盒一样被测试,只有在极少数情况下你才应该使用交互测试,在这种情况下,它绝对是至关重要的,以确保对象之间的某些交互以某种方式发生(例如,为了验证一个JavaScript订阅设计模式)。
无论如何你都能做到(不要!)
说了这么多,假设这样测试确实有意义(其实并没有!),一个提示:除了访问字段
args$2
,你还可以访问索引为1的声明字段。当然,按名称搜索也是可能的。无论如何,你必须反思lambda的类,获取你感兴趣的声明字段,使它们可访问(记住,它们是private final
),然后对它们各自的内容进行Assert。您也可以按字段类型进行过滤,以便对它们的顺序不那么敏感(此处未显示)。此外,我不明白为什么你创建一个
TestableExampleClass
,而不是使用原来的。在这个例子中,我使用显式类型而不仅仅是
def
,以便更容易理解代码的作用:字符串
或者,为了避免显式的
assert
而支持Spock风格的条件:型