Reputation: 494
I'm trying to use kotlintest along with Spring (not Spring Boot, just the standard spring-test). I'm finding it very difficult to do. Any pointers as to what I am doing wrong? I am somewhat of a Kotlin newbie as well so I could very well not be doing something correctly.
This is what I've tried so far:
import io.kotlintest.matchers.shouldBe
import io.kotlintest.specs.BehaviorSpec
import org.junit.ClassRule
import org.junit.Rule
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.rules.SpringClassRule
import org.springframework.test.context.junit4.rules.SpringMethodRule
open class SomeBean {
open fun sayHello() = "Hello"
}
@Configuration
open class TestConfig {
@Bean
open fun someBean(): SomeBean = SomeBean()
}
@ContextConfiguration(classes = arrayOf(TestConfig::class))
open class MyTests(var someBean: SomeBean) : BehaviorSpec() {
@Rule
@JvmField
val springMethodRule: SpringMethodRule = SpringMethodRule()
companion object {
@ClassRule
@JvmField
val SPRING_CLASS_RULE: SpringClassRule = SpringClassRule()
}
init {
given("A test") {
`when`("When my test happens") {
val hello = someBean.sayHello()
then("Hello should be valid") {
hello shouldBe "Hello"
}
}
}
}
}
Output:
java.lang.InstantiationException: MyTests
at java.lang.Class.newInstance(Class.java:427)
at io.kotlintest.KTestJUnitRunner.<init>(KTestJUnitRunner.kt:13)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy1.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchMethodException: MyTests.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 40 more
Another try:
import io.kotlintest.matchers.shouldBe
import io.kotlintest.specs.BehaviorSpec
import org.junit.ClassRule
import org.junit.Rule
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.rules.SpringClassRule
import org.springframework.test.context.junit4.rules.SpringMethodRule
open class SomeBean {
open fun sayHello() = "Hello"
}
@Configuration
open class TestConfig {
@Bean
open fun someBean(): SomeBean = SomeBean()
}
@ContextConfiguration(classes = arrayOf(TestConfig::class))
open class MyTests : BehaviorSpec() {
@Rule
@JvmField
val springMethodRule: SpringMethodRule = SpringMethodRule()
@Autowired
lateinit var someBean: SomeBean
companion object {
@ClassRule
@JvmField
val SPRING_CLASS_RULE: SpringClassRule = SpringClassRule()
}
init {
given("A test") {
`when`("When my test happens") {
val hello = someBean.sayHello()
then("Hello should be valid") {
hello shouldBe "Hello"
}
}
}
}
}
Output:
kotlin.UninitializedPropertyAccessException: lateinit property someBean has not been initialized
at MyTests.getSomeBean(SpringTests.kt:31)
at MyTests$1$1.invoke(SpringTests.kt:42)
at MyTests$1$1.invoke(SpringTests.kt:25)
at io.kotlintest.specs.BehaviorSpec$Given.when(BehaviorSpec.kt:27)
at MyTests$1.invoke(SpringTests.kt:41)
at MyTests$1.invoke(SpringTests.kt:25)
at io.kotlintest.specs.BehaviorSpec.given(BehaviorSpec.kt:18)
at MyTests.<init>(SpringTests.kt:40)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at io.kotlintest.KTestJUnitRunner.<init>(KTestJUnitRunner.kt:13)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy1.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:748)
This works, but then its not using kotlintest - its just using standard JUnit, which isn't what I want:
@ContextConfiguration(classes = arrayOf(TestConfig::class))
class Tests {
@Rule
@JvmField
val springMethodRule: SpringMethodRule = SpringMethodRule()
@Autowired
private lateinit var someBean: SomeBean
companion object {
@ClassRule
@JvmField
val SPRING_CLASS_RULE: SpringClassRule = SpringClassRule()
}
@Test
fun runTest(): Unit {
someBean.sayHello() shouldBe "Hello"
}
}
Upvotes: 5
Views: 7589
Reputation: 16387
Since KotlinTest 3.0.0 you can use the Spring extension listener.
This will auto wire up the class if you annotate your test class with @ContextConfiguration
.
For example:
@ContextConfiguration(classes = [(Components::class)])
class UserServiceTest : WordSpec() {
override fun listeners() = listOf(SpringListener)
@Autowired
var service: UserService? = null
init {
"SpringListener" should {
"have autowired the service" {
service!!.repository.findUser().name shouldBe "system_user"
}
}
}
}
You must add the kotlintest-extensions-spring
dependency to your build.
In the above code I added the SpringListener
only to a single class but you can use it project wide by adding it to your ProjectConfig
.
Full example is here: https://github.com/kotlintest/kotlintest/tree/master/kotlintest-samples/kotlintest-samples-spring
Upvotes: 9
Reputation: 31197
kotlintest does not support JUnit 4 rules.
Thus, what you are trying to do is simply not possible.
To use Spring's testing support with JUnit 4, you either have to use the SpringRunner
or the combination of SpringClassRule
and SpringMethodRule
(with a JUnit 4 Runner
implementation that supports JUnit 4 rules).
On the other hand, you could just switch to JUnit 5 and use the SpringExtension
. A lot of people seem to find that superior to using kotlintest for writing tests in Kotlin.
Upvotes: 1