Spring Boot 2.1 : Not loading property from application-test.yml - java

I am trying to read a property from application-test.yml during my unit test execution, but instead, the property from application-dev.yml is being read instead. I do not have a application.yml file. Appreciate the help.
AppProperties.java
#Component
#ConfigurationProperties(prefix="app")
public class AppProperties {
private String test;
public String getTest() {
return this.test;
}
public void setTest(String test) {
this.test = test;
}
}
application-dev.yml
spring:
profiles: dev
application:
name: testApplication
app:
test: 1
application-test.yml
spring:
profiles: test
application:
name: testApplication
app:
test: 2
AppServiceTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppProperties.class}, initializers= ConfigFileApplicationContextInitializer.class)
#EnableConfigurationProperties
#ActiveProfiles("test")
public class AppServiceTest{
#Autowired
AppProperties appProperties;
#Test
public void test(){
appProperties.getTest();
//This returns "1" instead of the desired "2"
}

Use #SpringBootTest annotation on unit test class
Spring Boot provides a #SpringBootTest annotation, which can be used as an alternative to the standard spring-test #ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests through SpringApplication. In addition to #SpringBootTest a number of other annotations are also provided for testing more specific slices of an application.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppProperties.class}, initializers=
ConfigFileApplicationContextInitializer.class)
#EnableConfigurationProperties
#SpringBootTest
#ActiveProfiles("test")
public class AppServiceTest{
#Autowired
AppProperties appProperties;
#Test
public void test(){
appProperties.getTest();
//This returns "1" instead of the desired "2"
}

Related

Testing #ConfigurationProperties annotation without loading Spring context

I'm trying to test a class that has the #ConfigurationProperties annotation but without loading the entire Spring context. I tried using only the JUnit5's features in order to do that but until now didn't succeed in that.
I'm using spring-boot-starter-parent v2.6.2 .
The class I'm testing :
#ConfigurationProperties("db.mongo")
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class MongoProperties {
private String host;
private String db;
private String user;
private String password;
}
The application.yaml :
db:
mongo:
host: localhost
db: test
user: test-user
password: secret
My Test class :
#ExtendWith(SpringExtension.class)
#EnableConfigurationProperties({MongoProperties.class})
public class MongoPropertiesTest {
#Autowired
private MongoProperties properties;
#Test
public void mongoPropertiesLoadedTest(){
assertNotNull(properties.getDb());
assertNotNull(properties.getHost());
assertNotNull(properties.getPassword());
assertNotNull(properties.getUser());
}
}
The MongoProperties bean is injected successfully, but all the values inside are null and the asserts fail.
Adding the #SpringBootTest solves the issue of the null values in the instance of the bean, but it also starts the whole spring context which is what I don't want.
But using #ExtendWith(SpringExtension.class) will also start the spring context. The difference is that it starts the context in a traditional way but #SpringBootTest starts it in a spring-boot way. So no matter you use which of them , it still requires to start the spring context.
If your concern is to minimise the number of beans required to be loaded into the spring context when using #SpringBootTest, you can actually configure a specified #Configuration like the following as by default #SpringBootTest will load all beans defined in your applications which may be too much for testing (see this for details) :
#SpringBootTest
public class MongoPropertiesTest {
#Autowired
private MongoProperties properties;
#Configuration
#EnableConfigurationProperties({MongoProperties.class})
public static class Config {
}
}
If you really want to just use #ExtendWith(SpringExtension.class) , you will lose the spring-boot feature such as externalising configuration features which cause you cannot load properties from application.properties and cannot support loading properties from YAML file etc. You have to manually configure ConfigDataApplicationContextInitializer to enable such features :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class)
public class MongoPropertiesTest {
#Autowired
private MongoProperties properties;
#Configuration
#EnableConfigurationProperties({MongoProperties.class})
public static class Config {
}
}
You can consider to further use #SpringJUnitConfig to combine #ExtendWith(SpringExtension.class) and #ContextConfiguration together which gives you :
#SpringJUnitConfig(initializers = ConfigDataApplicationContextInitializer.class)
public class MongoPropertiesTest {
#Autowired
private MongoProperties properties;
#Configuration
#EnableConfigurationProperties({MongoProperties.class})
public static class Config {
}
}
Actually both approaches do not have much differences in term of speed , so I prefer to just use #SpringBootTest for simplicity as it does not requires you to configure ConfigDataApplicationContextInitializer.

Spring JUnit5 test not loading resource values

I know there are a lot of questions regarding this, but all of them are suggesting to use #TestPropertySource and #EnableConfigurationProperties. I have already used them but still not working.
Config class - src/main/java/com/demo/config/AppConfig.java
#Configuration
#ConfigurationProperties(prefix = "api")
#Getter
#Setter
public class AppConfig {
private List<String> providers;
private boolean enabled;
}
Property source - src/test/resources/application-test.yml
api:
enabled: true
providers:
- prov1
- prov2
Test class - src/test/../MyTest.java
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = MyTestConfiguration.class)
class MyTest {
#Autowired
private AppConfig appConfig;
#Test
void runTest() {//some code with breakpoint}
}
Test configuraton - src/test/.../MyTestConfiguration.java
#TestConfiguration
#TestPropertySource(locations = "classpath:application-test.yml")
#EnableConfigurationProperties(value = AppConfig.class)
#ActiveProfiles("test")
public class MyTestConfiguration {
}
When I run the test, runTest() and inspect autowired appConfig value, the providers are empty and enabled is false. That means the values in yml file were not loaded.
I found a similar kind of question, but without answer.
I modified MyTest as #sergey-tsypanov suggested and then deleted MyTestConfiguration class. It worked and appConfig has values.
#SpringBootTest(classes = AppConfig.class)
#EnableAutoConfiguration
#ActiveProfiles("test")
class MyTest {
#Autowired
private AppConfig appConfig;
#Test
void runTest() {//some code with breakpoint}
}
It seems even if I don't have #SpringBootApplication, I can use #SpringBootTest and #EnableAutoConfiguration. I had spring Boot dependency in pom.xml
I think you need to replace
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = MyTestConfiguration.class)
with
#SpringBootTest(classes = {MyTestConfiguration.class})
Then application-test.yml will be picked up automatically.

SpringBoot #WebMvcTest is loading non-dependent beans when #ComponentScan is used along with #SpringBootApplication

I am using SpringBoot 2.3.3.RELEASE and I have following web controllers and Services.
myapp
- controllers
- ProductController
- OrderController
- services
- ProductService
- OrderService
ProductController only depends on ProductService and OrderController only depends on OrderService.
Following is my SpringBoot main entrypoint class:
package com.sivalabs.myapp;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have an #WebMvcTest controller for testing ProductController as follows:
#WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productService;
//some tests
}
Everything works perfectly fine with this configuration.
I am trying to use some external library with Spring components which has different package name, so I want to override #ComponentScan as follows:
package com.sivalabs.myapp;
import com.somelib.BeanConfig;
#SpringBootApplication
#ComponentScan(basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
When I include #ComponentScan on my main entrypoint class and run my #WebMvcTest based test ProductControllerTest then in addition to ProductService SpringBoot is trying to initialise OrderService also. Ideally ProductControllerTest should not load OrderService as ProductController doesn't depend on OrderService. Is it a bug?
Workarounds:
If I use #ComponentScan the way it is used on #SpringBootApplication meta-annotation and include basePackageClasses it is working fine.
package com.sivalabs.myapp;
#SpringBootApplication
#ComponentScan(excludeFilters = { #Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), #Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Instead of adding #ComponentScan on main entrypoint class if I add another configuration class and add #ComponentScan on that class then it's working fine.
package com.sivalabs.myapp.config;
#Configuration
#ComponentScan(basePackageClasses = {BeanConfig.class})
public class AppConfig {
}
Is it a bug in component scanning process or is it working as expected?
It working as expected.
#SpringBootAplication already have #ComponentScan with filters, and redeclare it as in workaroud #1 doesn't make sense, right?
Problem With original code is that #ComponentScan overrides exclude filters applied inside #SpringBootApplocation annotation. And stuff is not excluded, so when #WebMvc tries to request part of context using filters it fails, and entire context is loaded.
Just check what beans are initialized for context in each scenario. And stick to solution 2. #SpringBootTest will scan packages where application class is places, and any additional packages should be scanned by configurations. That is even more flixible right? :)

SpringJUnit4ClassRunner does not load the test properties file

I am trying to boot up a spring boot test, with a mapstruct implementation provided.
I am using the configuration below, but the application.properties file in the resources directory is not being read into the test.
How do I get the test below to read the default properties file, the file exist in test/resources and other spring boot tests are able to load it.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringBootExampleConfig.class)
public class SpringBootExampleTest {
#Autowired
private IMapper mapper;
#Test
public void test() {
}
#Configuration
#ComponentScan(basePackageClasses = SpringBootExampleTest.class)
public static class SpringTestConfig {
}
}

junit spring boot profile properties not being read

I am trring to read a value from the properties file in my junit setup using spring boot.
I can not read the value. Below is my content:-
application-test.properties
my.user.name=Amar
COnfig file to create beans:
#Configuration
#ActiveProfiles("test")
#Profile("test")
public class RdbmsTestConfig {
#Value("${my.user.name}")
private String name;
#Bean
public String myString(){
return "Amar";
}
#Bean
public PropsHolder propsHolder() {
PropsHolder propsHolder = new PropsHolder();
propsHolder.setUserName(name);
return propsHolder;
}
}
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RdbmsTestConfig.class)
#ActiveProfiles("test")
public class TestRoomService {
#Autowired
#Qualifier("myString")
private String myString;
#Autowired
private PropsHolder propsHolder;
#Autowired
private Environment env;
#Test
public void userTest() {
Arrays.stream(env.getActiveProfiles()).forEach(System.out::println);
System.out.println(propsHolder.getUserName());
Assert.assertNotNull(myString);
Assert.assertEquals("Amar",myString);
}
}
The value for propsHolder.getUserName comes out to be ${my.user.name}
First remove #ActiveProfiles("test") from your class RdbmsTestConfig. Then your test just defines the RdbmsTestConfig as spring context. As I can see you do not run a real spring boot test. The problem is you do not have any PropertyPlaceholderConfigurer configured in your spring config. So either configure one PropertyPlaceholderConfigurer or add #SpringBootTest to your test if you have any SpringBootApplication.
I've never used #Profile(), so i'm not sure if that is supposed to do what you want it to do. But I'm always using #PropertySources(), because otherwise, how is the code supposed to know where to look for the properties?
#Configuration
#PropertySources(value = { #PropertySource("classpath:core-
test.properties") })
I created a base test class that has the required annotations:-
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RdbmsTestConfig.class)
#ActiveProfiles("test")
#SpringBootTest
public abstract class BastTest { }
#ActiveProfiles set the profile to use used, I dont have to mention it in the application.properties file
My test class now extends this:-
public class TestRoomService extends BastTest
In my RdbmsTestConfig remove #ActiveProfiles annotation.

Categories