Getting properties in under test Service class by #Value returns null - spring-boot

I have a Spring Boot version 2.7.0 project with different profiles for dev and test with two different properties files: application-dev.properties and application-test.properties. (I have NO default application.properties file.)
In under test service class I have a property that I want to load its value from application-test.properties file. The service class:
#Service
#RequiredArgsConstructor
public class FileServiceImpl implements FileService {
#Value("${files.root-directory}")
private String fileDirectory;
#Override
#Transactional(readOnly = false)
public File createFile(CreateFileCommand command) {
var filePath = FileUtil.getPath(fileDirectory); // <- fileDirectory is null in tests
// ....
}
}
When I run the application in dev profile, everything is OK. But in tests, fileDirectory is always null.
Test class:
#SpringBootTest
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class FileServiceTest {
// ...
}
ApplicationTest class:
#SpringBootTest
#ActiveProfiles("test")
#TestPropertySource("classpath:application-test.properties")
#EnableConfigurationProperties
class ApiApplicationTests {
#Test
void contextLoads() {
}
}
application-dev.properties file:
files.root-directory=${user.home}\\api\\files
application-dev.properties file:
files.root-directory=/home/api/var/api/files
EDIT Screenshot of file structure

Put your properties file under src/test/resources for your (JUnit,Integration, Contract etc) test files. That should work!
Property files under src/main/resources are accessible to source files present under src/main/ only!

Related

properties from 'application.yml' not loading for standalone application when you try to run JUnit Test against it

I have class application which run fine when you run it as spring boot application and loads the properties from src/main/resources/application.yml like below
class abc{
#Value("${spring.host}")
private String host;
private ConnectionFactory getConnection() {
ConnectionFactory factory = new ConnectionFactory();//constructs Connection instances
factory.setHost(host);
return factory;
}
}
below is the application.yml details
spring.host: xx.xx.xx.1
But when you try to run junit test against above class abc then it doesn't upload the properties from src/main/resources/application.yml. Even i have also created a test file application.yml under test directory
src/test/resources/application.yml with the following details
spring.host: xx.xx.xx.1
Below is my Junit test class details
#RunWith(SpringRunner.class)
#TestPropertySource(properties = { "spring.config.location=classpath:application.yml" })
#TestPropertySource(properties = {"spring.test1= xx.xx.xx.2",
"spring.test2= 1111"
})
#ContextConfiguration(initializers = { ConfigFileApplicationContextInitializer.class })
public class Testclass {
InjectMocks
private abc ab;
#Value("${spring.test1}") /// here it is getting uploaded from src/main/resources/application.yml
private String test1;
#Test
public void testExecute() throws Exception {
abc.getConnection();
}
}
i am having hard time to understand and debug why the same configuration doesn't work from JUnit for loading properties from application.yml but it works well when simply run class abc as your standalone application.
Your using in abc
#Value("${spring.host}")
But in the application.yaml you have
spring.rabbitmq.host: xx.xx.xx.1

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

Values not passed into Component from application-dev.properties

I am writing tests in Junit and am using Spring Boot Framework (which I am new to) and need to use different urls to test different environments
Hence, I created 2 resource files in addition to application.properties
1> application-dev.properties
2> application-stage.properties
I created a component which I set the property values to be read into.
Lastly in my test file I am annotating the Test Class with:
#ContextConfiguration(classes = {ComponentName.class})
Also in my application.properties I have this line:
spring.profiles.active=dev
Expected:
When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
Why is the property not getting injected with the value from application-dev.properties?
I've tried several articles from Baeldung (A bit confusing though, the articles demonstrate multiple methods to do the same thing, but does not show 1 full technique to do everything end-to-end
https://www.baeldung.com/spring-profiles
I also tried with setting the active profile multiple ways:
a. Env variable
b. Using #ActiveProfiles annotation
Note: This is a Test Project (I am trying to automate-test a website, so all my resource files are inside src.test.resources
application.properties
#spring
https://ops.dev.website.us
spring.profiles.active=dev
url.sms=https://ops.default.website.us
application-dev.properties
url.sms=https://ops.dev.website.us
application-stage.properties
url.sms=https://ops.stage.website.us
Component File
#Component
#ConfigurationProperties(prefix = "url")
public class DevEnvironment {
private String sms;
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
}
Test File
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
#Autowired
private ConfigurableEnvironment env;
private DevEnvironment devEnvironment;
String url;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
System.out.println("Active profiles:" + env.getActiveProfiles()[0]);
devEnvironment = context.getBean(DevEnvironment.class);
//System.out.println("from set app context:" + devEnvironment.getSms());
url = devEnvironment.getSms();
}
#Test
public void testSms(){
System.out.println("inside test url:" +url);
}
}
Expected: When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
You forgot #SpringBootTest annotation to MyTest class.
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#SpringBootTest //add
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
//
}
I worked fine with this.
reference:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications

spring boot application context was not properly loaded if the yaml file wasn't application.yml

With following configuration, my test can read the properties from the yaml file correctly.
#SpringBootApplication
#PropertySource("classpath:application.yml")
#ComponentScan({ "com.my.service" })
public class MyApplication {
}
Then I renamed the yaml file to my-application.yml, and changed the PropertySource to
#PropertySource("classpath:my-application.yml")
Tests are failed due to the null property value. The configuration class is as following:
#Configuration
#ConfigurationProperties(prefix="my")
#Data
public class MyConfig {
private String attr1;
}
The test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
public class MyConfigTest {
#Autowired
private MyConfig myConfig;
#Test
public void getMyConfigTest() {
Assert.assertNotNull(myConfig.getAttr1());
}
Why spring boot can find the renamed yaml file, but it couldn't load the value correctly?
YAML files can’t be loaded via the #PropertySource annotation
It appears to work with #PropertySource("classpath:application.yml") because that's the default location and spring boot looks there regardless.
You may be able to use #ConfigurationProperties(location="claspath:my-application.yml") instead but it doesn't really achieve the same purpose (and I've never tried it myself).

Test Spring Configuration File

I have a spring configuration file with some beans defined in it, now I would like to use these beans in my unite/integration tests. Is there a way to do this without having a copy of the file in both the main resources and the test resources?
Your test class has to look like the following one:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:path/to/your/context.xml" })
public class MyTest {
#Autowired
private MyBean beanUnderTest;
}

Resources