55

What am I doing wrong? I'm using this little standalone App which runs and finds my src/main/resources/config/application.yml. The same configuration doesn't work from JUnit, see below:

@Configuration
@ComponentScan
@EnableConfigurationProperties

public class TestApplication {

    public static void main(String[] args) {

        SpringApplication.run(TestApplication.class);
    }
}


@Component
@ConfigurationProperties

public class Bean{
    ...
}

The following doesn't work, the same properties in application.yml are not loaded and Bean has only null values:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestApplication.class)

public class SomeTestClass {
    ...
}
luboskrnac
  • 21,083
  • 9
  • 71
  • 84
aliopi
  • 3,126
  • 1
  • 26
  • 23

10 Answers10

41

Try this:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplication.class, 
    initializers = ConfigFileApplicationContextInitializer.class)
public class SomeTestClass {
    ...
}

EDIT:

For Spring Boot version 1.5+, SpringApplicationConfiguration was removed in favour of SpringBootTest or direct use of SpringBootContextLoader.

You can still use initializers parameter with ContextConfiguration annotation.

luboskrnac
  • 21,083
  • 9
  • 71
  • 84
  • I used \@ContextConfiguration instead of \@SpringApplicationConfiguration and just added the initializers field, and that worked too. I don't seem to have SpringApplicationConfiguration in my classpath. – k-den Apr 04 '17 at 23:00
  • @k-den, you are probably already using Spring Boot 1.5.x, where SpringApplicationConfiguration was removed (it was deprecated since 1.4.x). – luboskrnac Apr 05 '17 at 07:43
21

The trick to load any custom yml file in SpringBoot 2.0 w/o using @SpringBootTest

  • create some yml file in test\resources
  • Use ConfigFileApplicationContextInitializer and spring.config.location property

Example Code:

@RunWith(SpringRunner.class)
@ContextConfiguration(
    classes = { MyConfiguration.class, AnotherDependancy.class },
    initializers = {ConfigFileApplicationContextInitializer.class} )
@TestPropertySource(properties = { "spring.config.location=classpath:myApp-test.yml" })
public class ConfigProviderTest {
    @Autowired
    private MyConfiguration myConfiguration; //this will be filled with myApp-test.yml 

   @Value("${my.config-yml-string}")
   private String someSrting; //will get value from the yml file.

}

For JUnit 5 use the @ExtendWith(SpringExtension.class) annotation instead of @RunWith(SpringRunner.class)

Tamas Kornai
  • 136
  • 1
  • 5
Yarix
  • 1,061
  • 10
  • 18
  • But you have to be beware of something... Since you are using a `@ContextConfiguration` that test will actually have a context. So the next test class will have the same context since Spring reuses it between tests, and when executed all at time, your test classes may fail as a side effect. To avoid that behaviour you have to also use `@DirtiesContext` along `@ContextConfiguration`. – Angel Pinazo Perpiñán Jul 26 '18 at 08:45
  • @AngelPinazo, you are correct, this example works for a single test execution in a hello world project, but not useful in the real world. – SergeyB Aug 01 '18 at 21:30
  • For those who want to actually understand more about [@DirtiesContext](https://www.javarticles.com/2016/03/spring-dirtiescontext-annotation-example.html) – Naruto Sempai Oct 02 '19 at 18:10
  • This is more suited for an integration test where you need the application context. Imho a unit test never needs a application context (unless it's a unit test for spring itself). Anyway I was looking for a integration test solution so thanks anyway – Pim Hazebroek Aug 09 '20 at 08:09
9

Here's another way: [Spring Boot v1.4.x]

@Configuration
@ConfigurationProperties(prefix = "own")
public class OwnSettings {

    private String name;
    Getter & setters...

}

import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@BootstrapWith(SpringBootTestContextBootstrapper.class)
public class OwnSettingsTest {

  @Autowired
  private OwnSettings bean;

  @Test
  public void test() {
    bean.getName();
  }
}

This works ONLY if also 'application.properties' file exists.

e.g. maven project:

src/main/resources/application.properties [ The file can be empty but it's mandatory! ]
src/main/resources/application.yml [here's your real config file]

Juha Hanka
  • 629
  • 6
  • 5
  • 1
    Thanks a ton! It worked without application.properties in 1.4.3.RELEASE. I only have application.yml under resources. – Shubham Feb 16 '17 at 09:30
  • You're welcome :-) Ou it's good to know that it works now without 'application.properties'. Thanks for the information. – Juha Hanka Feb 17 '17 at 12:01
  • What is `SpringBootTestContextBootstrapper.class` please? I don't have it in `org.springframework.boot:spring-boot:1.5.4.RELEASE` – Dimitri Kopriwa Jun 12 '17 at 12:05
  • 1
    Saved my day!! thanks a lot! @DimitriKopriwa you can add it via ` testCompile("org.springframework.boot:spring-boot-test:${springBootVersion}") ` – 62mkv Dec 27 '18 at 16:04
9

Alternative in February 2017:

@SpringBootTest
@ContextConfiguration(classes = { TestApplication.class })
@RunWith(SpringRunner.class)
public class SomeTestClass {
   ...
}

the lean variant (withouth @SpringBootTest):

@ContextConfiguration(classes = { TestApplication.class },
                 initializers = { ConfigFileApplicationContextInitializer.class })
@RunWith(SpringRunner.class)
public class SomeTestClass {
aliopi
  • 3,126
  • 1
  • 26
  • 23
5

Unit test with Spring Boot 2

spring boot 2 support 'application.properties' by default, for 'application.yml' just add below:

@TestPropertySource(properties = { "spring.config.location=classpath:application.yml" })

e.g.

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = { "spring.config.location=classpath:application.yml" })
public class ServiceTest {...}
Liam
  • 487
  • 4
  • 8
3

Spring boot 2 example:

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
        .withInitializer(new ConfigFileApplicationContextInitializer());

@Test public void test() throws Exception {

    this.contextRunner
    .withUserConfiguration(TestApplication.class)
    .run((context) -> {

        .....

    });
}
Ritesh
  • 7,085
  • 2
  • 35
  • 41
  • 2
    Thanks, it helped me, but in current Spring Boot version `ConfigFileApplicationContextInitializer` is deprecated. We can use `ConfigDataApplicationContextInitializer` instead and result will be the same. So we will have `private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withInitializer(new ConfigDataApplicationContextInitializer());`. – luke Jan 02 '21 at 12:38
1

In my case I was trying to test a library without a @SpringBootApp declared in the regular app classpath, but I do have one in my test context. After debugging my way through the Spring Boot initialization process, I discovered that Spring Boot's YamlPropertySourceLoader (as of 1.5.2.RELEASE) will not load YAML properties unless org.yaml.snakeyaml.Yaml is on the classpath. The solution for me was to add snakeyaml as a test dependency in my POM:

    <dependency>
        <groupId>org.yaml</groupId>
        <artifactId>snakeyaml</artifactId>
        <version>1.19</version>
        <scope>test</scope>
    </dependency>
Lyle
  • 3,374
  • 3
  • 22
  • 25
1

adding to Liam's answer, an alternative will be:

@TestPropertySource(locations = { "classpath:application.yaml" })

the key difference here is that the test will fail with a file not found exception if application.yaml is not in your /test/resources directory

Dexter Legaspi
  • 2,452
  • 1
  • 23
  • 24
0

extending Liam's answer

you can add spring.config.additional-location=classpath:application-overrides.yaml property so config from default location will be merged with the additional config provided:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = {
  "spring.config.additional-location=classpath:testcases/test-case-properties.yaml",
})
public class SpecificTestCaseIntegrationTest {
sergseven
  • 1
  • 1
-5

This works

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {

    @Test
    public void contextLoads() {
    }

}