Wordspec“应该”“何时”“在”

时间:2019-05-09 17:47:17

标签: scala lazy-loading lazy-evaluation scalatest lazy-initialization

我正在为我的一个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中,每个shouldwhenin范围实际上有何区别?我的印象是这些仅仅是逻辑上的差异,但是该测试表明,不同的事物在不同的时间在JVM代码的基础“静态”块中被初始化。

是否有人有进一步的阅读或指向Wordspec文档的链接,这些文档解释了这里发生的情况?

1 个答案:

答案 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 = ??
}

失败,因为从父级构造函数调用子级构造函数会导致一个循环。 shouldwhen

会发生这种情况
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的调用,因此不会触发循环。