Обновление В поисках оптимального решения я временно переключился на подход с использованием специальной фабрики тестов, которая выполняет инициализацию в сочетании с forAll. Таким образом, мне не нужно повторять код инициализации в каждой тестовой фабрике.
fun somePreStuffFactory() = funSpec {
beforeTest {
println("Do some init stuff here")
}
test("dummy test just to trigger beforeTest") {}
}
class MyTest : FunSpec({
runBlocking {
forAll(
row(MyTestFactory::someTest1),
row(MyTestFactory::someTest2)
) { testFactoryFunc ->
include(somePreStuffFactory())
include(testFactoryFunc())
}
}
})
Конец обновления
У меня много тестов на тестовых фабриках. Прежде чем запускать тесты внутри тестовой фабрики, мне нужно выполнить некоторую первоначальную настройку в начале каждой тестовой фабрики, и эта настройка одинакова для всех тестовых фабрик.
Проблема в том, что beforeSpec не вызывается внутри тестовой фабрики, поэтому в настоящее время я использую грязный обходной путь, выполняя инициализацию в первом тесте в каждой тестовой фабрике. Я был бы очень признателен за любые советы по этому поводу.
Оптимальным решением было бы иметь хук жизненного цикла внутри тестового класса, который запускается перед каждой тестовой фабрикой.
Код для воспроизведения
class MyTest : FunSpec({
include(someTest1())
include(someTest2())
})
import io.kotest.core.spec.style.funSpec
object MyTestFactory {
fun someTest1() = funSpec {
beforeSpec {
/** Not invoked */
println("Hello from someTest1#beforeSpec")
}
test("Init stuff is done inside a test") {
/** some init here */
}
test("first test") {
println("Hello from first test")
}
test("second test") {
println("Hello from second test")
}
}
fun someTest2() = funSpec {
beforeSpec {
/** Not invoked */
println("Hello from someTest2#beforeSpec")
}
test("Init stuff is done inside a test") {
/** some init here */
}
test("third test") {
println("Hello from third test")
}
}
}
Что я пробовал до сих пор
После повышения Kotest с 4.6.4 до 5.4.2 я смог запустить код в ответ от @ocos. Проблема в том, что BeforeSpecSample#beforeSpec вызывается только один раз, а не для каждой тестовой фабрики, что является моим требованием.
object BeforeSpecSample : BeforeSpecListener {
override suspend fun beforeSpec(spec: Spec) {
println("Hello from beforeSpec")
}
}
class MyTest : FunSpec({
extensions(BeforeSpecSample)
include(someTest1())
include(someTest2())
})
Обновлять
Прочитав этот выпуск GitHub, я успешно протестировал следующий подход, используя логическую переменную initialized и beforeTest. Было бы неплохо, если бы этот подход можно было использовать внутри тестового класса, а не внутри каждой тестовой фабрики, но без каких-либо ловушек жизненного цикла для тестовых фабрик я не понимаю, как это можно сделать.
fun someTest1() = funSpec {
var initialized = false
beforeTest {
if (!initialized) {
println("Hello from someTest1#beforeTest")
initialized = true
}
}
/** tests goes here */
}
Среда Котест 4.6.4, Котлин 1.7.10, Микронавт 3.6.3
@LeoColman: Спасибо за предложение. Я только что завершил тестирование beforeContainer как в тестовом классе, так и в тестовой фабрике. Ни один из них не был вызван. Причина этого, как я полагаю, в том, что в тестовом классе нет тестов, только включения. В тестовой фабрике beforeContainer страдает от той же проблемы, что и beforeSpec. BR





include использует только тесты и расширения, определенные в TestFactory вашего someTest(). Вот почему beforeSpec не вызывается.
Вы можете зарегистрировать extension в своем MyTest. Вот пример.
// you may use object MyTestFactory : BeforeSpecListener
object BeforeSpecSample : io.kotest.core.listeners.BeforeSpecListener {
override suspend fun beforeSpec(spec: Spec) {
println("-> before spec <-")
}
}
class MyTest : FunSpec({
extensions(BeforeSpecSample)
include(someTest())
test("my test 1") { println("my test 1") }
})
Спасибо за предложение. После повышения Kotest с 4.6.4 до 5.1.0 (более новые версии дают NoClassDefFound kotlinx/coroutines/CopyableThreadContextElement), я смог запустить ваш код. Проблема в том, что BeforeSpecSample#beforeSpec вызывается только один раз, а не для каждой тестовой фабрики, что является моим требованием. BR
Если вы не против сгруппировать все внутри тестовой фабрики в блок context, вы можете использовать BeforeContainerListener для инициализации:
object InitExtension : BeforeContainerListener {
override suspend fun beforeContainer(testCase: TestCase) {
if (testCase.parent == null) {
println("init stuff")
}
}
}
if (testCase.parent == null) просто позволяет вашей фабрике иметь другие вложенные context, которые не будут вызывать дополнительный вызов инициализации.
Затем вы можете написать свою фабрику следующим образом:
fun someTest1() = funSpec {
extension(InitExtension)
context("someTest1") {
test("first test") {
println("Hello from first test")
}
test("second test") {
println("Hello from second test")
}
}
}
Инициализация в InitExtension будет вызываться в начале contextsomeTest1 и может быть аналогичным образом включена в другие фабрики тестов, каждая из которых может быть инициализирована одним и тем же расширением.
Это работает, как и ожидалось, большое спасибо. Для других стоит упомянуть, что InitExtension также может быть определен как класс с параметрами конструктора в случае, например. услуги не требуются (как в моем случае). BR
Может быть, вы можете использовать прослушиватели контейнера до/после контейнера? Они могут помочь kotest.io/docs/framework/lifecycle-hooks.html