Constructor in Spring seems to be not working
See original GitHub issueHi all,
I’m working on a Spring Boot project and I wanted to give it a go with Kotlintest.
Now I’m running into an issue where autowiring is not working.
This is the code I’m using:
SimpleTest.kt
import io.kotlintest.AbstractProjectConfig
import io.kotlintest.extensions.ProjectLevelExtension
import io.kotlintest.shouldBe
import io.kotlintest.specs.StringSpec
import io.kotlintest.spring.SpringAutowireConstructorExtension
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.stereotype.Service
import org.springframework.test.context.ContextConfiguration
data class User(val name: String)
@SpringBootApplication
class MyApp
interface UserService {
fun getAnyUser(): User
}
@Service
class DummyUserService : UserService {
override fun getAnyUser(): User {
return User(name = "John Doe")
}
}
class MyProjectConfig : AbstractProjectConfig() {
override fun extensions(): List<ProjectLevelExtension> = listOf(SpringAutowireConstructorExtension)
}
In the same file I have three test classes
Field injection
@ContextConfiguration(classes = [(MyApp::class)])
class UserServiceTestFieldInjection : StringSpec() {
override fun listeners() = listOf(SpringListener)
@Autowired
lateinit var userService: UserService
init {
"get a user from the service" {
userService.getAnyUser().name shouldBe "John Doe"
}
}
}
This works as expected
Constructor injection with non-null constructor argument
@ContextConfiguration(classes = [(MyApp::class)])
class UserServiceTestConstructorInjection(userService: UserService) : StringSpec() {
init {
"get a user from the service" {
userService.getAnyUser().name shouldBe "John Doe"
}
}
}
This throws an exception because it’s missing a no-arg constructor:
11:31:32.627 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - configurationParameters=LauncherConfigurationParameters []
11:31:32.646 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - uniqueId=[engine:kotlintest]
11:31:32.715 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - LauncherDiscoveryRequest [
classpathRootSelectors=[]
classpathResourceSelectors=[]
classSelectors=[com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection]
methodSelectors=[]
directorySelectors=[]
fileSelectors=[]
moduleSelectors=[]
packageSelectors=[]
uniqueIdSelectors=[]
uriSelectors=[]
engineFilters=[]
postDiscoveryFilters=[]
classnameFilters=[]
packageNameFilters=[]
]
11:31:32.882 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - Loaded 1 classes from classnames...
11:31:33.635 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - ...which has filtered to 1 non abstract classes
11:31:33.640 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - 1 classes after applying discovery extensions11:31:33.700 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - configurationParameters=LauncherConfigurationParameters []
11:31:33.700 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - uniqueId=[engine:kotlintest]
11:31:33.700 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - LauncherDiscoveryRequest [
classpathRootSelectors=[]
classpathResourceSelectors=[]
classSelectors=[com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection]
methodSelectors=[]
directorySelectors=[]
fileSelectors=[]
moduleSelectors=[]
packageSelectors=[]
uniqueIdSelectors=[]
uriSelectors=[]
engineFilters=[]
postDiscoveryFilters=[]
classnameFilters=[]
packageNameFilters=[]
]
11:31:33.724 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - JUnit execution request [configurationParameters=LauncherConfigurationParameters []; rootTestDescriptor=KotlinTestEngineDescriptor: [engine:kotlintest]]
11:31:33.738 [main] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Engine started; classes=[[class com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection]]
11:31:33.740 [main] DEBUG io.kotlintest.runner.jvm.TestEngine - Submitting 1 specs
11:31:33.745 [main] DEBUG io.kotlintest.runner.jvm.TestEngine - Waiting for spec execution service to terminate
11:31:33.777 [main] ERROR io.kotlintest.runner.jvm.TestEngine - Error during test engine run
java.lang.IllegalArgumentException: Class should have a single no-arg constructor: class com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection
at kotlin.reflect.full.KClasses.createInstance(KClasses.kt:281)
at io.kotlintest.runner.jvm.JvmKt.instantiateSpec(jvm.kt:12)
at io.kotlintest.runner.jvm.TestEngine.createSpec(TestEngine.kt:99)
at io.kotlintest.runner.jvm.TestEngine.access$createSpec(TestEngine.kt:14)
at io.kotlintest.runner.jvm.TestEngine$submitSpec$1.run(TestEngine.kt:77)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
11:31:33.779 [main] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Engine finished; throwable=[java.lang.IllegalArgumentException: Class should have a single no-arg constructor: class com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection]
java.lang.IllegalArgumentException: Class should have a single no-arg constructor: class com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection
at kotlin.reflect.full.KClasses.createInstance(KClasses.kt:281)
at io.kotlintest.runner.jvm.JvmKt.instantiateSpec(jvm.kt:12)
at io.kotlintest.runner.jvm.TestEngine.createSpec(TestEngine.kt:99)
at io.kotlintest.runner.jvm.TestEngine.access$createSpec(TestEngine.kt:14)
at io.kotlintest.runner.jvm.TestEngine$submitSpec$1.run(TestEngine.kt:77)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 255
Empty test suite.
So I figured to make the constructor argument nullable with a default null value. i.e. provide a default constructor.
Constructor injection with nullable constructor argument
@ContextConfiguration(classes = [(MyApp::class)])
class UserServiceTestConstructorInjection2(userService: UserService? = null) : StringSpec() {
init {
"get a user from the service" {
userService!!.getAnyUser().name shouldBe "John Doe"
}
}
}
This throws a nice Kotlin.NullPointerException
11:35:02.950 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - configurationParameters=LauncherConfigurationParameters []
11:35:02.959 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - uniqueId=[engine:kotlintest]
11:35:02.984 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - LauncherDiscoveryRequest [
classpathRootSelectors=[]
classpathResourceSelectors=[]
classSelectors=[com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection2]
methodSelectors=[]
directorySelectors=[]
fileSelectors=[]
moduleSelectors=[]
packageSelectors=[]
uniqueIdSelectors=[]
uriSelectors=[]
engineFilters=[]
postDiscoveryFilters=[]
classnameFilters=[]
packageNameFilters=[]
]
11:35:03.221 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - Loaded 1 classes from classnames...
11:35:04.121 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - ...which has filtered to 1 non abstract classes
11:35:04.125 [main] DEBUG io.kotlintest.runner.jvm.TestDiscovery - 1 classes after applying discovery extensions11:35:04.192 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - configurationParameters=LauncherConfigurationParameters []
11:35:04.192 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - uniqueId=[engine:kotlintest]
11:35:04.193 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - LauncherDiscoveryRequest [
classpathRootSelectors=[]
classpathResourceSelectors=[]
classSelectors=[com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection2]
methodSelectors=[]
directorySelectors=[]
fileSelectors=[]
moduleSelectors=[]
packageSelectors=[]
uniqueIdSelectors=[]
uriSelectors=[]
engineFilters=[]
postDiscoveryFilters=[]
classnameFilters=[]
packageNameFilters=[]
]
11:35:04.197 [main] DEBUG io.kotlintest.runner.junit5.KotlinTestEngine - JUnit execution request [configurationParameters=LauncherConfigurationParameters []; rootTestDescriptor=KotlinTestEngineDescriptor: [engine:kotlintest]]
11:35:04.237 [main] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Engine started; classes=[[class com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection2]]
11:35:04.239 [main] DEBUG io.kotlintest.runner.jvm.TestEngine - Submitting 1 specs
11:35:04.273 [main] DEBUG io.kotlintest.runner.jvm.TestEngine - Waiting for spec execution service to terminate
11:35:04.322 [pool-1-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - prepareSpec [Description(parents=[], name=UserServiceTestConstructorInjection2)]11:35:04.362 [pool-2-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Creating test case descriptor Description(parents=[UserServiceTestConstructorInjection2], name=get a user from the service)/Test
11:35:04.364 [pool-2-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Notifying junit of start event [engine:kotlintest]/[spec:UserServiceTestConstructorInjection2]/[test:get a user from the service]
11:35:04.371 [pool-1-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - completeTestCase Description(parents=[UserServiceTestConstructorInjection2], name=get a user from the service) with result TestResult(status=Error, error=kotlin.KotlinNullPointerException, reason=null, metaData={})
11:35:04.371 [pool-1-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - completeSpec [Description(parents=[], name=UserServiceTestConstructorInjection2)]
11:35:04.374 [pool-1-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Notifying junit of test case completion [engine:kotlintest]/[spec:UserServiceTestConstructorInjection2]/[test:get a user from the service]=TestResult(status=Error, error=kotlin.KotlinNullPointerException, reason=null, metaData={})
kotlin.KotlinNullPointerException
at com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection2$1.invoke(SImpleTest.kt:65)
at com.brabantia.bw.bw6tests.UserServiceTestConstructorInjection2$1.invoke(SImpleTest.kt:61)
at io.kotlintest.runner.jvm.TestCaseExecutor$executeTestSet$1.run(TestCaseExecutor.kt:96)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
11:35:04.401 [pool-1-thread-1] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Notifying junit that spec finished [engine:kotlintest]/[spec:UserServiceTestConstructorInjection2] TestExecutionResult [status = SUCCESSFUL, throwable = null]11:35:04.403 [main] DEBUG io.kotlintest.runner.junit5.JUnitTestRunnerListener - Engine finished; throwable=[null]
Process finished with exit code 255
In Maven I have the following configuration
<properties>
<kotlin.version>1.2.41</kotlin.version>
<kotlintest.version>3.1.8</kotlintest.version>
</properties>
<dependencies>
<!-- KOTLIN -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<!-- SPRING -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SPRING TEST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- TEST -->
<dependency>
<groupId>io.kotlintest</groupId>
<artifactId>kotlintest-runner-junit5</artifactId>
<version>${kotlintest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.kotlintest</groupId>
<artifactId>kotlintest-extensions-spring</artifactId>
<version>${kotlintest.version}</version>
</dependency>
</dependencies>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
Am I missing something here or is this a genuine bug?
Issue Analytics
- State:
- Created 5 years ago
- Comments:11 (8 by maintainers)
Top Results From Across the Web
Spring constructor injection not working - Stack Overflow
I got this error when I hover the mouse on my XML code in Eclipse IDE: cvc-complex-type.2.4.a: Invalid content was found starting with...
Read more >[JVM][Spring] Constructor injection doesn't seem to work
I've been trying to understand the Cucumber/Spring integration (helped massively by Paolo) and have hit an issue. Everything's working fine for field ...
Read more >Constructor Dependency Injection in Spring - Baeldung
Quick and practical intro to Constructor based injection with Spring. ... The problem is, of course, when things fall apart in production -...
Read more >Java: How to fix Spring @Autowired annotation not working ...
When a constructor of a class is called, the @Autowired instance variables do not contain their values yet. If you are dependent on...
Read more >Top 10 Most Common Spring Framework Mistakes - Toptal
This is obviously not desirable since tests should not only verify the correctness of your code, but also serve as documentation on how...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Hi,
Did you place
MyProjectConfig
inside theio.kotlintest.provided
package ?As I just stumbled here, let me quote the essential part of the documentation to make this “magic” work:
https://github.com/kotlintest/kotlintest/blob/master/doc/reference.md#project-config