question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Constructor in Spring seems to be not working

See original GitHub issue

Hi 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:closed
  • Created 5 years ago
  • Comments:11 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
sksamuelcommented, Jul 16, 2018

Hi,

Did you place MyProjectConfig inside the io.kotlintest.provided package ?

0reactions
mindhaqcommented, Feb 8, 2019

As I just stumbled here, let me quote the essential part of the documentation to make this “magic” work:

To do this, create an object that is derived from AbstractProjectConfig, name this object ProjectConfig and place it in a package called io.kotlintest.provided. KotlinTest will detect it’s presence and use any configuration defined there when executing tests.

https://github.com/kotlintest/kotlintest/blob/master/doc/reference.md#project-config

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found