简体   繁体   中英

Spring Boot test with yaml properties by profile

So there's a lot of hits on this topic, but none of them have worked for me.

I have a very simple configuration class:

@Configuration
@ConfigurationProperties(prefix = "props")
public class TagIncluder {

    private static final String PARAMETER_NAME = "tags";

    private List<String> tags;

    public TagIncluder() {
        tags = new ArrayList<>();
    }

    public List<String> getTags() {
        return tags;
    }

    @Handler
    public void attachIncludedTags(Exchange exchange) {
        exchange.getIn().setHeader(PARAMETER_NAME, tags);
    }
}

I want this class to be able to load different property files. I am using yaml, and my file is named application-tag_test.yml . I have tried placing this file in src/main/resources , src/test/resources and src/test/resources/config , but it is never picked up.

This is the contents of the property file:

props:
  tags:
    - test 

And finally, the test case:

@SpringBootTest
@ActiveProfiles("tag_test")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TagIncluder.class)
public class TagIncluderTest extends ExchangeTestSupport {

    @Autowired
    private TagIncluder sut;

    @Test
    public void attachIncludedTags_shouldUseTagsInFileIfFileSpecified() {
        Exchange testExchange = createExchange();
        sut.attachIncludedTags(testExchange);

        Assertions.assertThat(testExchange.getIn().getHeader("tags", List.class))
            .size().isGreaterThanOrEqualTo(1);
    }
}

Additionally, I have tried placing an application.properties file in the above described locations with the following content:

spring.profiles.active=tag_test

What is required for Spring to set my yaml file as the desired configuration for my test class under test?

UPDATE

So after some exploration and trial and error, I have found that the following works:

@SpringBootTest
@ActiveProfiles("tag_test")
@RunWith(SpringJUnit4ClassRunner.class)
public class TagIncluderTest extends ExchangeTestSupport {

    @Autowired
    private TagIncluder sut;

    @Test
    public void attachIncludedTags_shouldUseTagsInFileIfFileSpecified() {
        Exchange testExchange = createExchange();
        sut.attachIncludedTags(testExchange);

        Assertions.assertThat(testExchange.getIn().getHeader("tags", List.class))
            .size().isGreaterThanOrEqualTo(1);
    }
}

The difference here is that I've removed the @ContextConfiguration annotation and I let Spring take care of all of that.

It is a lot slower, and I would prefer specifying what is needed. I think this might break in the future, for instance if I add another configuration class that will start with the entire context and throw errors because those properties are not included in my application-tag_test.yml configuration.

Finally, any of the above locations I tried for the configuration is valid with the above annotations. The application.properties to specify a profile is not needed.

If anyone knows a way to specify what should be loaded into the context instead, I'd be very grateful for another solution.

With some guidance of Jans suggestion above, I've managed to isolate the test to a slice. Auto configured testing is written about here , however that only touches on Springs predefined @..Test annotations.

If you dive deeper into the @WebMvcTest , for instance, you will find the @ImportAutoConfiguration annotation.

Using this, we can tell our test class to enable auto configuration for a single slice of our application. A tutorial is available here . The full list of factories available for auto configuration can be found in the spring-boot repository .

Finally, this is the entire test class:

@ActiveProfiles("tag_test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TagIncluder.class)
@ImportAutoConfiguration(classes = ConfigurationPropertiesAutoConfiguration.class)
public class TagIncluderTest extends ExchangeTestSupport {

    @Autowired
    private TagIncluder sut;

    @Test
    public void attachIncludedTags_shouldUseTagsInFileIfFileSpecified() {
        Exchange testExchange = createExchange();
        sut.attachIncludedTags(testExchange);

        Assertions.assertThat(testExchange.getIn().getHeader("tags", List.class))
            .size().isGreaterThanOrEqualTo(1);
    }
}

The class under test is untouched.

So now we can:

  • Use profiles
  • Use yaml
  • Test only our desired class in Spring Context

This has been very enlightening :)

The Spring Boot Test documentations states that

External properties, logging, and other features of Spring Boot are installed in the context by default only if you use SpringApplication to create it.

This means that you need to have a working Spring Boot Application in order to test anything related to property loading in a test case.

Also, setting a list from properties needs a setter. This works:

@Configuration
@ConfigurationProperties(prefix = "props")
public class TagIncluder {
    private List<String> tags;

    public void setTags(List<String> tags) {
        this.tags = tags;
    }

    public List<String> getTags() {
        return tags;
    }
}

@Component
public class MyComponent {
    @Autowired
    TagIncluder tagIncluder;
}

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootTest
@ActiveProfiles("tag_test")
@RunWith(SpringRunner.class)
public class TagIncluderTest {

    @Autowired
    private TagIncluder sut;

    @Test
    public void attachIncludedTags_shouldUseTagsInFileIfFileSpecified() {
        System.out.println(sut.getTags());
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM