Wordspec "должен" "когда" "в" - PullRequest
2 голосов
/ 09 мая 2019

Я инициализирую сопутствующий объект для одного из моих наборов тестов 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.Мой вопрос: что на самом деле отличает каждую из should, when, in областей в Wordspec?У меня сложилось впечатление, что это были просто логические различия, но этот тест показывает, что разные вещи инициализируются в разное время в базовом «статическом» блоке кода JVM.

Есть ли у кого-нибудь дальнейшее чтение или ссылкик документации Wordpec, которая объясняет, что здесь происходит?

1 Ответ

1 голос
/ 10 мая 2019

@ БогданВакуленко показывает как следующий дизайн

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, следовательно, цикл не запускается.

...