我正在尝试将Scala的Future与ScalaTest和Mockito一起使用,但是通过一个非常简单的测试用例,我无法验证Future内部模拟函数的任何调用。
Timelist = []
for row_cells in sheetname.iter_rows(min_col=1,max_col=6,min_row=2):
Timelist = row_cells[1].value
for x in Timelist:
print(x)
每次都会失败,并显示以下错误:
import org.mockito.Mockito.{timeout, verify}
import org.scalatest.FunSpec
import org.scalatest.mockito.MockitoSugar
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class FutureTest extends FunSpec with MockitoSugar {
it("future test") {
val mockFunction = mock[() => Unit]
Future {
mockFunction()
}
verify(mockFunction, timeout(1000)).apply()
}
}
我已经测试了它在没有未来的情况下可以工作。
让我最惊讶的是,如果我还在Future块中也包含一个print语句,它每次都会成功,如下所示:
Wanted but not invoked:
function0.apply$mcV$sp();
-> at test.FutureTest.$anonfun$new$1(FutureTest.scala:18)
However, there was exactly 1 interaction with this mock:
function0.apply();
-> at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:658)
您对问题是什么以及为什么打印声明在这里很重要吗?
我正在使用:
答案 0 :(得分:1)
该错误表明apply$mcV$sp()
未被调用,因此让我们尝试分别比较两种情况下-Xprint:jvm
的输出以查看其调用位置:
给出
Future {
mockFunction()
println("test")
}
-Xprint:jvm
的输出是
final <static> <artifact> def $anonfun$new$2(mockFunction$1: Function0): Unit = {
mockFunction$1.apply$mcV$sp();
scala.Predef.println("test")
};
final <static> <artifact> def $anonfun$new$1($this: FutureTest): Unit = {
val mockFunction: Function0 = $this.mock((ClassTag.apply(classOf[scala.Function0]): scala.reflect.ClassTag)).$asInstanceOf[Function0]();
scala.concurrent.Future.apply({
$anonfun(mockFunction)
}, scala.concurrent.ExecutionContext$Implicits.global());
org.mockito.Mockito.verify(mockFunction, org.mockito.Mockito.timeout(1000L)).$asInstanceOf[Function0]().apply$mcV$sp()
};
有
Future {
mockFunction()
}
-Xprint:jvm
的输出是
final <static> <artifact> def $anonfun$new$1($this: FutureTest): Unit = {
val mockFunction: Function0 = $this.mock((ClassTag.apply(classOf[scala.Function0]): scala.reflect.ClassTag)).$asInstanceOf[Function0]();
scala.concurrent.Future.apply(mockFunction, scala.concurrent.ExecutionContext$Implicits.global());
org.mockito.Mockito.verify(mockFunction, org.mockito.Mockito.timeout(1000L)).$asInstanceOf[Function0]().apply$mcV$sp()
};
请注意,mockFunction
的调用方式不同
Future.apply({$anonfun(mockFunction) ...
Future.apply(mockFunction ...
在第一种情况下,它作为参数传递给$anonfun
,它的确调用了apply$mcV$sp()
,如下所示:
mockFunction$1.apply$mcV$sp();
在第二种情况下找不到apply$mcV$sp()
的调用。
使用Future.successful { mockFunction() }
似乎可以使它工作,并且我们看到apply$mcV$sp()
被按需调用
final <static> <artifact> def $anonfun$new$1($this: FutureTest): Unit = {
val mockFunction: Function0 = $this.mock((ClassTag.apply(classOf[scala.Function0]): scala.reflect.ClassTag)).$asInstanceOf[Function0]();
scala.concurrent.Future.successful({
mockFunction.apply$mcV$sp();
scala.runtime.BoxedUnit.UNIT
});
org.mockito.Mockito.verify(mockFunction, org.mockito.Mockito.timeout(1000L)).$asInstanceOf[Function0]().apply$mcV$sp()
};
apply$mcV$sp
首先来自哪里?检查Function0
trait Function0[@specialized(Specializable.Primitives) +R] extends AnyRef { self =>
def apply(): R
override def toString() = "<function0>"
}
我们看到@specialized(Specializable.Primitives)
会导致
abstract trait Function0 extends Object { self: example.Fun =>
def apply(): Object;
override def toString(): String = "<function0>";
<specialized> def apply$mcZ$sp(): Boolean = scala.Boolean.unbox(Fun.this.apply());
<specialized> def apply$mcB$sp(): Byte = scala.Byte.unbox(Fun.this.apply());
<specialized> def apply$mcC$sp(): Char = scala.Char.unbox(Fun.this.apply());
<specialized> def apply$mcD$sp(): Double = scala.Double.unbox(Fun.this.apply());
<specialized> def apply$mcF$sp(): Float = scala.Float.unbox(Fun.this.apply());
<specialized> def apply$mcI$sp(): Int = scala.Int.unbox(Fun.this.apply());
<specialized> def apply$mcJ$sp(): Long = scala.Long.unbox(Fun.this.apply());
<specialized> def apply$mcS$sp(): Short = scala.Short.unbox(Fun.this.apply());
<specialized> def apply$mcV$sp(): Unit = {
Function0.this.apply();
()
};
def /*Fun*/$init$(): Unit = {
()
}
};
我们看到的apply$mcV$sp
依次称为实际的apply
<specialized> def apply$mcV$sp(): Unit = {
Function0.this.apply();
()
};
这些似乎是问题的一部分,但是我没有足够的知识来将它们组合在一起。在我看来,Future(mockFunction())
应该可以正常工作,因此我们需要一个更有知识的人来解释它。在此之前,请尝试使用Future.successful
作为解决方法。
答案 1 :(得分:1)
正如@ mario-galic正确指出的,这是由于调用了编译器生成的综合方法,而不是我们(和Mockito)所期望的。
恐怕无法用Java版本的Mockito解决它,因为它不了解Scala编译器所做的所有额外工作。
mockito-scala 1.5.2解决了Scala 2.12和2.13的这一问题,因为它知道如何适当地处理所述合成方法。我建议您将其替换为mockito-core,以避免出现此问题和许多其他问题。
答案 2 :(得分:0)
尝试以下。
val f: Future = Future {
mockFunction()
}
f onComplete {
verify(mockFunction, timeout(1000)).apply()
}