@ БогданВакуленко показывает как следующий дизайн
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
}
}
потому что, несмотря на то, что они принимают параметр pass-by-name 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
, следовательно, цикл не запускается.