3

I have a class that is injected with the application.yaml properties located in the src/main/resources directory. I wrote a test that is asserting if those properties are injected as expected. The test does work fine but it is using the application.yaml file from src/main/resources.

I want it to use the test-application.yaml file from src/test/resources to use some bogus values.

I followed many guides and read many questions here on Stackoverflow but could not get any of those approaches to work. Maybe because I mixed up different solution's annotations or whatever.

Status Quo

I am using the spring versions provided by the spring management system

    id("io.spring.dependency-management") version "1.0.9.RELEASE"

Edit: Also I use the following relevant dependencies:

    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    }
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-webflux")

The property file src/main/resources/application.yaml:

controlroom:
  ftpConnections:
    foo-importer:
      host: ftp.stage.company.com
      port: 22
      user: foo-ftp
      password: password
    bar-importer:
      host: ftp.stage.company.com
      port: 22
      user: bar-ftp
      password: password

This is the property configuration holder class:

@Configuration
@ConfigurationProperties(prefix = "controlroom")
class FtpConnectionsProvider(
) {
    lateinit var ftpConnections: Map<String, Map<String, String>>
}

And this is the test class that currently is injecting the properties from main/resources/application.yaml:

@ExtendWith(SpringExtension::class)
@SpringBootTest
internal class FtpConnectionsProviderTest(
        @Autowired
        val ftpConnectionsProvider: FtpConnectionsProvider
) {
    @Test
    fun `should fill FtpConnectionsProvider with properties from yaml`() {
        assertThat(ftpConnectionsProvider.ftpConnections["foo-importer"]).containsAllEntriesOf(
                mapOf(
                        "host" to "ftp.stage.company.com",
                        "port" to "22",
                        "user" to "foo-ftp",
                        "password" to "password"
                ))
        assertThat(ftpConnectionsProvider.ftpConnections["bar-importer"]).containsAllEntriesOf(
                mapOf(
                        "host" to "ftp.stage.company.com",
                        "port" to "22",
                        "user" to "bar-ftp",
                        "password" to "password"
                ))
    }
}

How to use the test-application.yaml ?

Now I wonder how I can give the test the instruction to only use src/test/resources/test-application.yaml.

First Try

@ExtendWith(SpringExtension::class)
@SpringBootTest
@TestPropertySource("classpath:test-application.yaml")
internal class FtpConnectionsProviderTest

Result: Just ignores it and keeps using src/main/resources/application.yaml

Second Try

People say that @TestPropertySource can't read .yaml files and therefore I added the @ContextConfiguration annotation since this was the suggested fix.

@ExtendWith(SpringExtension::class)
@SpringBootTest
@TestPropertySource("classpath:test-application.yaml")
@ContextConfiguration(initializers=[ConfigFileApplicationContextInitializer::class])
internal class FtpConnectionsProviderTest

Result: Just ignores it and keeps using src/main/kotlin/resources/application.yaml

Nothing seems to work. I do not grasp why this is such a big deal and why it is so complicated to find a solution

xetra11
  • 7,671
  • 14
  • 84
  • 159
  • are you sure to have [SnakeYaml](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-yaml) on your classpath? – xerx593 Mar 07 '20 at 11:09
  • @xerx593 Yes. https://imgur.com/a/dANSEdf – xetra11 Mar 07 '20 at 11:14
  • no-no, sorry, the image is no proof: you show me/us a `*.yaml` file ...i/we need: a dependency on `spring-boot[-...]-starter` / `snake-yaml` in your pom.xml/build.gradle. – xerx593 Mar 07 '20 at 11:16
  • @xerx593 please see my edit on status quo regarding the dependencies I use. I want to mention again that the injection of .yaml file does work but I can't refer to the one in the `src/test/kolin/resources` directory – xetra11 Mar 07 '20 at 11:24
  • ..ok, then another "eye-catcher": why is it `src/test/ko[t]lin/resources` ?? it is "no standard" ...if you didn't configure in your pom/gradle (then the yaml file never makes it to "classpath"). I would expect: `src/test/resources/**` or `src/main/resources/**`. – xerx593 Mar 07 '20 at 11:35
  • ..definitly: don't put your yaml-files into "source" folders (src/[main|test]/[java|kotlin]), but all to "resources" folder (language independent: src/[main|test]/resources) (and please check the "output folder" target/[test-]classes for yaml files) – xerx593 Mar 07 '20 at 11:38
  • @xerx593 sorry I was in a haste while typing. Surely I follow the proper convention for resources `src/test/resources` – xetra11 Mar 07 '20 at 11:55
  • 1
    thx! :) (1 "side effect" excluded) – xerx593 Mar 07 '20 at 11:58

2 Answers2

2

You cannot load yaml file using @TestPropertySource.

Madhu Bhat
  • 13,559
  • 2
  • 38
  • 54
Betlista
  • 10,327
  • 13
  • 69
  • 110
  • Completely impossible? How can I then bring in a `.yaml` properties file for testing purpose – xetra11 Mar 07 '20 at 11:16
  • 1
    Easiest is to use standard application.yml file in a test folder (I assume you are uing Maven), instead of different file, use different profile for tests subset, which needs to be configured differently... – Betlista Mar 07 '20 at 11:29
  • 2
    that's quite a good "summary" from today's p.o.v, but still a "never ending story" :) ... https://stackoverflow.com/q/21271468/592355 – xerx593 Mar 07 '20 at 11:57
1

Have you tried placing a application.yaml file under src/test/kotlin/resources? That should work without any special config / annotations.

Also, if you want to have / override certain properties within a yaml file called testfoo, you can place these props in a application-testfoo.yaml in the same directory, and the file will be used if you activate a testfoo Spring profile.

Example:

src/test/kotlin/resources/application.yaml:

controlroom:
  ftpConnections:
    foo-importer:
      host: ftp.stage.company.com
      port: 22
      user: foo-ftp
      password: password
    bar-importer:
      host: ftp.stage.company.com
      port: 22
      user: bar-ftp
      password: password

src/test/kotlin/resources/application-testfoo.yaml:

controlroom:
  ftpConnections:
    foo-importer:
      user: test-foo-ftp

If you active the testfoo profile, controlroom.ftpConnections.foo-importer.user will be overridden. Is that what you want?

Alternatively, as an even simpler solution if you just want to replace a couple of your production prop values: Don't create a src/test/kotlin/resources/application.yaml at all. Just create the src/test/kotlin/resources/application-testfoo.yaml and it should override the values from src/main/kotlin/resources/application.yaml (assuming the testfoo profile is active)! That works in Java with my applications.properties anyways - I see no reason why it should work any differently in Kotlin.

Joel
  • 724
  • 5
  • 12
  • wouldn't this end up in a package called `resources` in `target/test-classes` folder? – xerx593 Mar 07 '20 at 11:43
  • this does work indeed @Joel The thing is I want to split up different `test-foo-application.yaml` for readability reasons – xetra11 Mar 07 '20 at 11:47
  • Readability of the yaml files? If so, have you considered using a spring profile named `testfoo` for example and naming the yaml file `application-testfoo.yaml` (under the same test directory)? If you use that naming convention, Spring should find it. You can have a application.yaml file and any values in the application-testfoo.yaml file will override them. – Joel Mar 07 '20 at 11:50