我正在为我的一个scala测试套件初始化一个伴随对象。此伴随对象中的字段之一是延迟求值的,并使用测试套件中的某些字段进行初始化。像这样:
class SomeClassSpec extends WordSpec with Matchers with OneInstancePerTest {
lazy val someFieldTheCompanionObjectNeeds = "some_field_I_need"
"my test class" should {
"do something interesting" when {
"I tell it to" in {
//a bunch of test code using the SomeTestClassCompanionObject.someConfigurationINeed field.
}
}
}
}
object SomeTestClassCompanionObject extends SomeClassSpec {
lazy val someConfigurationINeed = Config(SomeTestClass.someFieldTheCompanionObjectNeeds)
}
不要问。我知道这是不好的做法,但是必须这样做,这与我的问题无关。
我在这里注意到的是,如果我尝试在测试的SomeTestClassCompanionObject.someConfigurationINeed
块中使用它,我的when
字段未初始化,但是 却在in
块。我的问题是:在Wordspec中,每个should
,when
,in
范围实际上有何区别?我的印象是这些仅仅是逻辑上的差异,但是该测试表明,不同的事物在不同的时间在JVM代码的基础“静态”块中被初始化。
是否有人有进一步的阅读或指向Wordspec文档的链接,这些文档解释了这里发生的情况?
答案 0 :(得分:1)
@BogdanVakulenko shows以下设计如何
class SomeClassSpec {
SomeTestClassCompanionObject.someConfigurationINeed // NullPointerException or StackOverflowError because calling child's constructor which in turn calls parent's constructor
}
object SomeTestClassCompanionObject extends SomeClassSpec {
lazy val someConfigurationINeed = ??
}
失败,因为从父级构造函数调用子级构造函数会导致一个循环。 should
和when
class SomeClassSpec {
"my test class" should {
SomeTestClassCompanionObject.someConfigurationINeed // error
}
"do something interesting" when {
SomeTestClassCompanionObject.someConfigurationINeed // error
}
}
因为尽管他们采用了传递参数f
,但只有在使用时才进行评估
def should(right: => Unit)
def when(f: => Unit)
它们导致对registerNestedBranch的调用,该调用确实对f
进行求值,从而触发了循环
def registerNestedBranch(description: String, childPrefix: Option[String], fun: => Unit, registrationClosedMessageFun: => String, sourceFile: String, methodName: String, stackDepth: Int, adjustment: Int, location: Option[Location], pos: Option[source.Position]): Unit = {
...
try {
fun // Execute the function
}
...
}
另一方面,in
class SomeClassSpec {
"I tell it to" in {
SomeTestClassCompanionObject.someConfigurationINeed // ok
}
}
它也有f
个名字
def in(f: => Any /* Assertion */)
因为它导致对registerTest
的调用,该调用仅注册函数值f
以便执行,但是在f
传递给注册时,绝对不会得到评估。然后,单独的Runner
对象实际上运行f
,但此时在SomeTestClassCompanionObject.someConfigurationINeed
的构造函数之外执行对SomeClassSpec
的调用,因此不会触发循环。