Load different application.yml in SpringBoot Test

Spring MvcSpring Boot

Spring Mvc Problem Overview


I'm using a spring boot app which runs my src/main/resources/config/application.yml.

When I run my test case by :

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
public class MyIntTest{
}

The test codes still run my application.yml file to load properties. I wonder if it is possible to run another *.yml file when running the test case.

Spring Mvc Solutions


Solution 1 - Spring Mvc

One option is to work with profiles. Create a file called application-test.yml, move all properties you need for those tests to that file and then add the @ActiveProfiles annotation to your test class:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
@ActiveProfiles("test") // Like this
public class MyIntTest{
}

Be aware, it will additionally load the application-test.yml, so all properties that are in application.yml are still going to be applied as well. If you don't want that, either use a profile for those as well, or override them in your application-test.yml.

Solution 2 - Spring Mvc

You can set your test properties in src/test/resources/config/application.yml file. Spring Boot test cases will take properties from application.yml file in test directory.

The config folder is predefined in Spring Boot.

As per documentation:

> If you do not like application.properties as the configuration file name, you can switch to another file name by specifying a spring.config.name environment property. You can also refer to an explicit location by using the spring.config.location environment property (which is a comma-separated list of directory locations or file paths). The following example shows how to specify a different file name:

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

The same works for application.yml

Documentation:

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-application-property-files

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files

Solution 3 - Spring Mvc

You can use @TestPropertySource to load different properties/yaml file

@TestPropertySource(locations="classpath:test.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class MyIntTest{

}

OR if you want to override only specific properties/yaml you can use

@TestPropertySource(
        properties = {
                "spring.jpa.hibernate.ddl-auto=validate",
                "liquibase.enabled=false"
        }
)

Solution 4 - Spring Mvc

If you need to have production application.yml completely replaced then put its test version to the same path but in test environment (usually it is src/test/resources/)

But if you need to override or add some properties then you have few options.

Option 1: put test application.yml in src/test/resources/config/ directory as @TheKojuEffect suggests in his answer.

Option 2: use profile-specific properties: create say application-test.yml in your src/test/resources/ folder and:

  • add @ActiveProfiles annotation to your test classes:

      @SpringBootTest(classes = Application.class)
      @ActiveProfiles("test")
      public class MyIntTest {
    
  • or alternatively set spring.profiles.active property value in @SpringBootTest annotation:

      @SpringBootTest(
              properties = ["spring.profiles.active=test"],
              classes = Application.class,
      )
      public class MyIntTest {
    

This works not only with @SpringBootTest but with @JsonTest, @JdbcTests, @DataJpaTest and other slice test annotations as well.

And you can set as many profiles as you want (spring.profiles.active=dev,hsqldb) - see farther details in documentation on Profiles.

Solution 5 - Spring Mvc

Spring-boot framework allows us to provide YAML files as a replacement for the .properties file and it is convenient.The keys in property files can be provided in YAML format in application.yml file in the resource folder and spring-boot will automatically take it up.Keep in mind that the yaml format has to keep the spaces correct for the value to be read correctly.

You can use the @Value("${property}") to inject the values from the YAML files. Also Spring.active.profiles can be given to differentiate between different YAML for different environments for convenient deployment.

For testing purposes, the test YAML file can be named like application-test.yml and placed in the resource folder of the test directory.

If you are specifying the application-test.yml and provide the spring test profile in the .yml, then you can use the @ActiveProfiles('test') annotation to direct spring to take the configurations from the application-test.yml that you have specified.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApplicationTest.class)
@ActiveProfiles("test")
public class MyTest {
 ...
}

If you are using JUnit 5 then no need for other annotations as @SpringBootTest already include the springrunner annotation. Keeping a separate main ApplicationTest.class enables us to provide separate configuration classes for tests and we can prevent the default configuration beans from loading by excluding them from a component scan in the test main class. You can also provide the profile to be loaded there.

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

Here is the link for Spring documentation regarding the use of YAML instead of .properties file(s): https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Solution 6 - Spring Mvc

See this: https://stackoverflow.com/questions/21271468/spring-propertysource-using-yaml

I think the 3rd answer has what you're looking for, i.e have a separate POJO to map your yaml values into:

@ConfigurationProperties(path="classpath:/appprops.yml", name="db")
public class DbProperties {
    private String url;
    private String username;
    private String password;
...
}

Then annotate your test class with this:

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}

Solution 7 - Spring Mvc

Starting with Spring 4.1, We can directly set the property in application.yml using the @TestPropertySource annotation.

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = {"yoursection.yourparameter=your_value"})
public MyIntTest
{
 //your test methods
}

Just convert your yaml parameters into complete property structure. For example: If content of application.yml is like below

yoursection:
  yourparameter:your_value

Then value to go inside the @TestPropertySource will be,

yoursection.yourparameter=your_value

Solution 8 - Spring Mvc

A simple working configuration using

@TestPropertySource and properties

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(properties = {"spring.config.location=classpath:another.yml"})
public class TestClass {


    @Test
   
    public void someTest() {
    }
}

Solution 9 - Spring Mvc

This might be considered one of the options. now if you wanted to load a yml file ( which did not get loaded by default on applying the above annotations) the trick is to use

@ContextConfiguration(classes= {...}, initializers={ConfigFileApplicationContextInitializer.class})

Here is a sample code

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@DirtiesContext
@ContextConfiguration(classes= {DataSourceTestConfig.class}, initializers = {ConfigFileApplicationContextInitializer.class})
public class CustomDateDeserializerTest {
	
	
	private ObjectMapper objMapper;
	
	@Before
	public void setUp() {
		objMapper = new ObjectMapper();
		
	}
	
	@Test
	public void test_dateDeserialization() {
		
	}
}

Again make sure that the setup config java file - here DataSourceTestConfig.java contains the following property values.

@Configuration
@ActiveProfiles("test")
@TestPropertySource(properties = { "spring.config.location=classpath:application-test.yml" })
public class DataSourceTestConfig implements EnvironmentAware {
	
	private Environment env;
	
	@Bean
	@Profile("test")
	public DataSource testDs() {
       HikariDataSource ds = new HikariDataSource();
		
		boolean isAutoCommitEnabled = env.getProperty("spring.datasource.hikari.auto-commit") != null ? Boolean.parseBoolean(env.getProperty("spring.datasource.hikari.auto-commit")):false;
		ds.setAutoCommit(isAutoCommitEnabled);
		// Connection test query is for legacy connections
		//ds.setConnectionInitSql(env.getProperty("spring.datasource.hikari.connection-test-query"));
		ds.setPoolName(env.getProperty("spring.datasource.hikari.pool-name"));
		ds.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
		long timeout = env.getProperty("spring.datasource.hikari.idleTimeout") != null ? Long.parseLong(env.getProperty("spring.datasource.hikari.idleTimeout")): 40000;
		ds.setIdleTimeout(timeout);
		long maxLifeTime = env.getProperty("spring.datasource.hikari.maxLifetime") != null ? Long.parseLong(env.getProperty("spring.datasource.hikari.maxLifetime")): 1800000 ;
		ds.setMaxLifetime(maxLifeTime);
		ds.setJdbcUrl(env.getProperty("spring.datasource.url"));
		ds.setPoolName(env.getProperty("spring.datasource.hikari.pool-name"));
		ds.setUsername(env.getProperty("spring.datasource.username"));
		ds.setPassword(env.getProperty("spring.datasource.password"));
		int poolSize = env.getProperty("spring.datasource.hikari.maximum-pool-size") != null ? Integer.parseInt(env.getProperty("spring.datasource.hikari.maximum-pool-size")): 10;
		ds.setMaximumPoolSize(poolSize);
		
		return ds;
	}
	
	@Bean
	@Profile("test")
	public JdbcTemplate testJdbctemplate() {
		return new JdbcTemplate(testDs());
	}
	
	@Bean
	@Profile("test")
	public NamedParameterJdbcTemplate testNamedTemplate() {
		return new NamedParameterJdbcTemplate(testDs());
	}

	@Override
	public void setEnvironment(Environment environment) {
		// TODO Auto-generated method stub
		this.env = environment;
	}
}

Solution 10 - Spring Mvc

We can use @SpringBootTest annotation which loads the yml file from src\main\java\com...hence when we execute the unit test, all of the properties are already there in the config properties class.

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

    @InjectMocks
    AddressFieldsValidator addressFieldsValidator;

    @Autowired
    AddressFieldsConfig addressFieldsConfig;
    ...........
    
    @Before
    public void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
        ReflectionTestUtils.setField(addressFieldsValidator,"addressFieldsConfig", addressFieldsConfig);
    }

}

We can use @Value annotation if you have few configs or other wise we can use a config properties class. For e.g

@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "address.fields.regex")
public class AddressFieldsConfig {

    private int firstName;
    private int lastName;
    .........
    

Solution 11 - Spring Mvc

Lu55 Option 1 how to...

Add test only application.yml inside a seperated resources folder.

├── main
│   ├── java
│   └── resources
│       ├── application.yml
└── test
├── java
└── resources
└── application.yml

In this project structure the application.yml under main is loaded if the code under main is running, the application.yml under test is used in a test.

To setup this structure add a new Package folder test/recources if not present.

Eclipse right click on your project -> Properties -> Java Build Path -> Source Tab -> (Dialog ont the rigth side) "Add Folder ..."

Inside Source Folder Selection -> mark test -> click on "Create New Folder ..." button -> type "resources" inside the Textfeld -> Click the "Finish" button.

After pushing the "Finisch" button you can see the sourcefolder {projectname}/src/test/recources (new)

Optional: Arrange folder sequence for the Project Explorer view. Klick on Order and Export Tab mark and move {projectname}/src/test/recources to bottom. Apply and Close

!!! Clean up Project !!!
Eclipse -> Project -> Clean ...

Now there is a separated yaml for test and the main application.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionExiaView Question on Stackoverflow
Solution 1 - Spring Mvcg00glen00bView Answer on Stackoverflow
Solution 2 - Spring MvcTheKojuEffectView Answer on Stackoverflow
Solution 3 - Spring MvcA0__oNView Answer on Stackoverflow
Solution 4 - Spring MvcLu55View Answer on Stackoverflow
Solution 5 - Spring MvcAnanthapadmanabhanView Answer on Stackoverflow
Solution 6 - Spring MvcPeteView Answer on Stackoverflow
Solution 7 - Spring Mvcgirivasan4View Answer on Stackoverflow
Solution 8 - Spring MvcDhruvView Answer on Stackoverflow
Solution 9 - Spring Mvcvijayakumarpsg587View Answer on Stackoverflow
Solution 10 - Spring MvcdenzalView Answer on Stackoverflow
Solution 11 - Spring MvcPinocchioView Answer on Stackoverflow