kotest: lateinit variable doesn't work with interceptTestCase

I have a problem with init lateinit var property in interceptTestCase method. Here is my example:

class ExampleTest : StringSpec() {
    private lateinit var someString: String

    override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
        println("Start Testcase")
        someString = "Hello"
        test()
        println("End Testcase")
    }

    init {
        "Hello should equal to Hello" {
            someString shouldEqual "Hello"
        }
    }
}

And here is out put:

Start Testcase

kotlin.UninitializedPropertyAccessException: lateinit property someString has not been initialized

	at com.locata.myapplication.ExampleTest.access$getSomeString$p(ExampleTest.kt:7)
	at com.locata.myapplication.ExampleTest$1.invoke(ExampleTest.kt:18)
	at com.locata.myapplication.ExampleTest$1.invoke(ExampleTest.kt:7)
	at io.kotlintest.Spec$runTest$callable$1$1.invoke(Spec.kt:124)
	at io.kotlintest.Spec$runTest$callable$1$1.invoke(Spec.kt:15)
	at io.kotlintest.Spec$runTest$initialInterceptor$1$1.invoke(Spec.kt:116)
	at io.kotlintest.Spec$runTest$initialInterceptor$1$1.invoke(Spec.kt:15)
	at com.locata.myapplication.ExampleTest.interceptTestCase(ExampleTest.kt:12)
	at io.kotlintest.Spec$runTest$initialInterceptor$1.invoke(Spec.kt:116)
	at io.kotlintest.Spec$runTest$initialInterceptor$1.invoke(Spec.kt:15)
	at io.kotlintest.Spec$runTest$callable$1.call(Spec.kt:124)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)


Process finished with exit code 255

It’s happen for both 2.0.0 and 2.0.1.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 15 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Proposed solution works only with some specs.

works with StringSpec

class MyTests : BehaviorSpec() {
    lateinit var profilePresenter: ProfilePresenter
    @Mock lateinit var mockEventBus: EventBus

    override val oneInstancePerTest = false

    override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
        MockitoAnnotations.initMocks(this)

        profilePresenter = ProfilePresenter(mockEventBus)

        test() // don't forget to call test()!
    }

    init {
        "Should register event buss" {
            profilePresenter.takeView(mockView)
            verify(mockEventBus).register(profilePresenter)
        }
    }
}

but it fails to work with BehaviorSpec

class MyTests : BehaviorSpec() {
    lateinit var profilePresenter: ProfilePresenter
    @Mock lateinit var mockEventBus: EventBus

    override val oneInstancePerTest = false

    override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
        MockitoAnnotations.initMocks(this)

        profilePresenter = ProfilePresenter(mockEventBus)

        test() // don't forget to call test()!
    }

    init {
        Given("ProfilePresenter") {

            When("take view") {
                profilePresenter.takeView(mockView)

                Then("register event bus") {
                    verify(mockEventBus).register(profilePresenter)
                }
            }
        }
    }
}

kotlin.UninitializedPropertyAccessException: lateinit property profilePresenter has not been initialized

I strongly feel that this is really big issue, because it’s SUPER unintuitive. To be honest I don’t get the idea behind and the problem with oneInstancePerTest. Your docs say: oneInstancePerTest - By default a single instance of the test class is created for all the test it contains. This seems fine for me. What is the problem by simply calling interceptTestCase before each test and initialize\reinitialize lateinit property before each test? How this relates to single/multiple instances of test class? Why exactly we need to disable it?

Please solve this issue!

[Edit 10 mins later]

With this piece of code you can intercept the () -> Unit block like a middleware and run it before any variable initialization.

infix fun String.test(block: () -> Unit): TestCase = this {
    //before initialization
    block()
    //after ..
  }

The use case is:

"init some variables before this code" test {
        // your test ... 
      }

This is a hotfix for this problem, you can rename the method to any other name…

well done @sksamuel 👏