spring-bootkotlinapache-cameljunit5

All Autowired and MockBean injection not working after upgrading to camel-test-spring-junit5


I'm upgrading an old application from camel 2.22.0 to camel 3.14.10 (Yes, I'm late, I know).

This includes updating from camel-test-spring to camel-test-spring-junit5.

After a couple of changes to annotations and some other things, I had a compiling build again. But the tests all fail, and the reason is that none of my @Autowired and @MockBean properties actually contain anything. They're all uninitialized.

Here's a short example of what the tests look like now and what they looked like before.

Now (injection not working):

@CamelSpringBootTest
@ActiveProfiles("test")
@TestPropertySource(properties = ["ftp.enabled=true"])
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
class ImageGatewayApplicationTest {

    @MockBean
    private lateinit var authenticationService: AuthenticationService
    @MockBean
    private lateinit var panoramaService: PanoramaService
    @MockBean
    private lateinit var bmetryService: BmetryEventService
    @Autowired
    private lateinit var template: ProducerTemplate
    @Autowired
    private lateinit var restTemplate: TestRestTemplate

    @EndpointInject(uri = "mock:new-image")
    private lateinit var newImageMock: MockEndpoint

    @Test
    fun `upload with ftp`() {
        val resource = ClassPathResource(TEST_PNG)
        val tempFile = FileHelper.createTempFile(resource)
        template.sendBodyAndHeader(TEST_FTP, tempFile, FILE_NAME, TEST_PNG) // <- template not initialised
        // verify mock
        verify<AuthenticationService>(authenticationService).checkCredentials(TEST_USER, TEST_PW)
        // clean up
        tempFile.delete()
    }

Here are the annotations as they were before the update. Nothing except the annotations changed (injection worked perfectly fine):

@RunWith(CamelSpringRunner::class)
@ActiveProfiles("test")
@TestPropertySource(properties = ["ftp.enabled=true"])
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
class ImageGatewayApplicationTest { }

The precise error I'm getting is this:

kotlin.UninitializedPropertyAccessException: lateinit property restTemplate has not been initialized

Note that it is not complaining about not finding beans or not being able to instantiate them or any of the stuff one might expect. It's as though the annotations are simply ignored. When I pause the code and look at the class, none of them are initialised. Nothing's being injected.

I'm halfways certain that I'm missing some annotation, but I can't find it. I have tried adding @EnableAutoConfiguration, @ContextConfiguration and @UseAdviceWith (as that is in fact used in some tests), all with no result.

Additional data: Turns out it's running on JUnit4

Prompted by sidgates comment, I took a look at the complete stacktrace. I wasn't even aware there's more of it, Intellij for some reason saw fit to hide it by default. This revealed something rather surprising, and something that probably explains my predicament: The whole thing is still running on JUnit4!

kotlin.UninitializedPropertyAccessException: lateinit property restTemplate has not been initialized

    at webcam.yellow.gateway.ImageGatewayApplicationTest.upload with http(ImageGatewayApplicationTest.kt:82)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:42)
    at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:80)
    at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:72)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

That's a step in the right direction, but try as I might, I can't get it to run with Junit5. Honestly I had thought it already did, as the spring-boot version (2.7.18) does support it (spring-boot 2.2+).

It turns out that the old kotlin-test dependency in there forced junit4 to run. Once I replaced it with a newer kotest-junit5-runner dependency the tests finally run. They don't check out yet, though, there's still plenty of issues with camel it seems, but it looks like this is another kind of animal...


Solution

  • It turns out that while I was assuming that the tests were running on JUnit5 due to the spring-boot version, they were in fact still running on junit-vintage.

    Responsible for that was another outdated dependency on kotlintest. Once I replaced that with kotest-runner-junit5-jvm, the problem was gone.

    Still lots of issues left with the camel tests, but that's another problem.