Create Spring Boot test with separate application.properties - java

I am developing web app with Spring Boot, and now I am trying to create tests for DAO layer, and I'd like to use different configurations, which will read custom property file instead of standard one. But Iam having trouble with that, it always reads default application. and hibernate.properties.
The want to do it in order to have different hibernate.ddl-auto properties for test. But when I run the test, I see that Spring reads properties from the hibernate.properties which is in resource folder (I've purposely made a typo in that file in order to get exception if it was read by Spring). But why does it read that file even when I use #TestPropertySource? I see there's something that I don`t know about that, but what?
package src/test/java/com.guard/dao
#RunWith(SpringRunner.class)
#DataJpaTest
#Rollback
public class LifeguardDaoTest {
#Autowired
private LifeguardDao lgDao;
#Test
public void selectTest(){
for (Lifeguard lg :lgDao.getAll()) {
System.out.println(lg);
}
}
}`
Test configuration class is to setup context
package src/test/java/com.guard
#SpringBootApplication
#EntityScan(value = {"com.guard.dao","com.guard.model"})
#TestPropertySource(value = {"classpath:application-test.properties", "classpath:hibernate-test.properties"})
public class TestConfiguration {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(TestConfiguration.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
System.out.println("Spring boot test generated " + beanNames.length + " beans");
}
}
Required application-test.properties and hibernate-test.properties are on src/test/java path
Here's project structure (don`t know how to design it here, sorry)
|src
|--main
|----java
|------com.guard
|----------configuration
|-------------GuardApplication.class (#SpringBootApplication,requires default props)
|--test
|----java
|------application-test.properties
|-------hibernate-test.properties
|-----com.guard
|-------TestConfiguration.class
|-------dao
|---------LifeguardDaoTest.class
My application-test.properties
`
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.jpa.hibernate.show_sql=false
spring.jpa.hibernate.format_sql=true
spring.jpa.hibernate.hbm2ddl-auto=create
# even in case if it won`t use "spring.jpa" prefix
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.show_sql=true
hibernate.format_sql=true
`

Create new resources directory inside test directory and put your test properties file there. Also rename your properties files to application.properties and hibernate.properties
Spring tests will take properties from test/resources/ directory. And in this approach, you do not need #TestPropertySource

Typically, #TestPropertySource is used in conjunction with #ContextConfiguration.
Try with this configuration class.
#Configuration
#ComponentScan
#Profile("test")
public class TestConfiguration {
}
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ContextConfiguration(classes = TestConfiguration.class)
#ActiveProfiles("test")
#TestPropertySource(locations="classpath:application-test.properties")
public #interface IntegrationTest { }
Then you just write test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#IntegrationTest
public class SomeDaoTest {
...
}

Related

Spring boot how to pick externalized spring properties file

I have this configurations which needs to be used for a spring boot application.
server.port=8085
server.servlet.context-path=/authserver
#data source
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=<url>
spring.datasource.username=<username>
spring.datasource.password=<password>
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
By default spring-boot picks up up the application.properties file located in src/main/resources/
I want to alter this path and direct spring boot to different application.properties file
I can achieve this using
java -jar app.jar --spring.config.location=classpath:/another-location.properties
Is there any any alternative solution I can achieve this without passing args through command line?
I was using this
#PropertySource("file:C:\Users\test\.test\test.properties")
#ConfigurationProperties(prefix = "spring")
public class Configuration {
private String ddlAuto;
private String url;
private String username;
private String password;
private String driverClassName;
}
in my Main class
#SpringBootApplication
#EnableConfigurationProperties(Configuration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
There after I tried executing the app commenting out all datasource properties in application.properties under src/main/resources/
But it keeps giving me the error mentioned bellow and application fails to start
I was referring this tutorial : https://www.mkyong.com/spring-boot/spring-boot-configurationproperties-example/
but as it's mentioned I get this error when i start the spring boot application
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException:
Any help on this would be appreciated
The recommended way to have externalized properties is to use the spring.config.location system property, by starting your application like so:
java -jar -Dspring.config.location=/path/to/my/file.properties app.jar
The reason for this is that you don't add coupling between your code and your filesystem hierarchy.
Before Spring Boot 2.0 this property is additive, meaning that it will complement the default locations. After Spring Boot 2.0, spring.config.location replaces the default locations (e.g. classpath src/main/resources/application.properties). To keep the additive behaviour after 2.0, use spring.config.additional-location instead.
Please see here for official documentation on this matter.
I am able to make it work properly on Spring Boot 2.1.2.RELEASE. This is what I have done:
I have a test.properties in my /tmp folder with the following content:
test.myprop=hello
I also have the usual property file in the resources folder:
myprop=world
I have created a class for the custom property file:
#Configuration
#PropertySource("file:/tmp/test.properties")
#ConfigurationProperties(prefix = "test")
public class TestConfig {
private String myprop;
public String getMyprop() {
return myprop;
}
public void setMyprop(String myprop) {
this.myprop = myprop;
}
}
And then in my main class I have enabled to configuration properties:
#EnableConfigurationProperties(TestConfig.class)
#SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Now I have this test controller:
#RestController
public class TestController {
#Value("${test.myprop}")
private String externalisedProp;
#Value("${myprop}")
private String prop;
#GetMapping("test")
public void test() {
System.out.println("externalised: " + externalisedProp);
System.out.println("not externalised" + prop);
}
}
Which, once called, is properly printing:
externalised: hello
not externalised: world
My TestConfig class is in the same package as the MyApp main class.
What I have done is very similar, almost identical, to your solution, are you sure your path is correct? Also, I can see that the content of your property file is not matching what you have in your config class, the prefix is different. Maybe that is the problem?
Edit:
I have tried to remove the #Configuration annotation from my property class (which you do not have as well) and it is not able to pick up the externalised properties anymore. The error is different though but you should try to add it.

How to load a properties file based on the server environment with spring so that the values can be injected?

To my surprise I have had a difficult time finding an answer to this question. I have Seen many examples where you can use #PropertySource to load a specific properties file for a class. I have also seen examples where you can easily add different property files in spring boot projects. But what I want to do is to do this for a spring project that is NOT spring boot and load a properties file so that the values of this file can be injected in classes annotated with #Component which is dependent on the server environment. So for example if I am on development server I want a particular properties file loaded and on production a different properties file. The reason that I am doing it like this is because my data and service layers are their own modules. These modules contain their own unit tests and can be imported as their own modules in other spring boot projects. I need properties files to be loaded to serve these modules which use spring but not spring boot. I have tried the following, but this does not work.
#Configuration
#Profile("test")
#EnableJpaRepositories("com.hi.repository")
#EnableTransactionManagement
#EnableScheduling
public class InfrastructureConfig {
...
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Map<String, String> env = System.getenv();
String propertiesFile=null;
String e = env.get("SERVER_ENV");
if (e.equals("dev")) {
propertiesFile = "environment/development.properties";
} else if (e.equals("prod")) {
propertiesFile = "environment/production.properties";
}
configurer.setLocation(new ClassPathResource(propertiesFile));
return configurer;
}
Then I have a test which looks like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
#ActiveProfiles("test")
public class LogTest {
private static final Logger log = LogManager.getLogger(LogTest.class);
#Autowired
PathsService pathsService;
#Autowired
Environment environment;
#Test
public void testBeans(){
System.out.println("********** WASSUP from LogTest");
System.out.println(environment.getProperty("imageBucket"));
}
Although the test prints out null which indicates to me the properties file has not been loaded and prepared for its values to be injected. How can I achieve this?
You don't really need to set properties yourself, but you can do this using spring configuration. Check the documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
If you're using spring boot - all you need to do is create multiple properties file for your environments. And only for properties you need to override.
So your main properties file would be at
src/main/resources/application.properties
Production
src/main/resources/application-prod.properties
Development
src/main/resources/application-dev.properties
Testing
src/main/resources/application-test.properties
And then just use the profile name as your environment variable
java -jar -Dspring.profiles.active=prod demo-0.0.1-SNAPSHOT.jar
Actually, you can just use a placeholder in #PropertySource annotation.
See documentation:
Any ${...} placeholders present in a #PropertySource resource location will be resolved against the set of property sources already registered against the environment.
Assuming that placeholder is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value.
I've made a simple example, it receives a 'property.environment' value to choose, which .properties file should be used as property source. I have two resource files in my classpath - application-test.properties and application-dev.properties, each one contains a 'test.property' value ('test-env' and 'dev-env' respectively).
Property configuration:
#Configuration
#PropertySource("classpath:/config/application-${property.environment}.properties")
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
return propertySourcesPlaceholderConfigurer;
}
}
Component with #Value
#Component
public class TestService {
#Value("${test.property}")
String testProperty;
#PostConstruct
void init() {
System.out.println("---------------------------------------------------------");
System.out.println("Running in " + testProperty + " environment");
System.out.println("---------------------------------------------------------");
}
}
Build command line example (it runs tests with test environment properties)
mvn clean install -DargLine="-Dproperty.environment=test"
Output
---------------------------------------------------------
Running in test-env environment
---------------------------------------------------------
Run command line example
java -jar -Dproperty.environment=dev PATH_TO_YOUR_JAR.jar
Output
---------------------------------------------------------
Running in dev-env environment
---------------------------------------------------------
Don't hard code based on different environment, in spring boot you can able to maintain properties specific environment easily. Refer https://spapas.github.io/2016/03/31/spring-boot-settings/
I would try to take advantage of the profile mechanism already in place in Spring. You basically have done the job yourself already, the only thing you need to change is to have different configurations for "test" and "production" profiles. I prefer to keep everything related to test away from production code (allowing me to place the TestConfig class below in the test source path), so I would probably do something like this:
#Configuration
#Profile("!test")
#PropertySource(value = "classpath:/environment/production.properties")
#Import(AppConfig.class)
public class ProductionConfig
{
// Your production-specific config goes here
}
#Configuration
#Profile("test")
#PropertySource(value = "classpath:/environment/development.properties")
#Import(AppConfig.class)
public class TestConfig
{
// Your test-specific config goes here
}
#Configuration
public class AppConfig
{
// Needed for spring to handle ${property:default} syntax
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you prefer to have one config for both cases, you can let the AppConfig import the TestConfig and the ProductionConfig instead, but that will put test code in to production...
Good luck with your project!

Spring Boot integration tests doesn't read properties files

I would like to create integration test in which Spring Boot will read a value from .properties file using #Value annotation.
But every time I'm running test my assertion fails because Spring is unable to read the value:
org.junit.ComparisonFailure:
Expected :works!
Actual :${test}
My test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebTests.ConfigurationClass.class, WebTests.ClassToTest.class})
public class WebTests {
#Configuration
#ActiveProfiles("test")
static class ConfigurationClass {}
#Component
static class ClassToTest{
#Value("${test}")
private String test;
}
#Autowired
private ClassToTest config;
#Test
public void testTransferService() {
Assert.assertEquals(config.test, "works!");
}
}
application-test.properties under src/main/resource package contains:
test=works!
What can be the reason of that behavior and how can I fix it?
Any help highly appreciated.
You should load the application-test.properties using #PropertySource or #TestPropertySource
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(locations="classpath:application-test.properties")
#ContextConfiguration(classes = {WebTests.ConfigurationClass.class, WebTests.ClassToTest.class})
public class WebTests {
}
for more info: Look into this Override default Spring-Boot application.properties settings in Junit Test
Besides the above marked correct answer, there is another nature way to load application-test.properties: Set your test run "profile" to "test".
Mark your test cases with:
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
application-xxxx.properties is a naming convention for properties of different "profile".
This file application-xxxx.properties should be placed in src/main/resources folder.
"Profile" is also useful in bean configuration.

Spring #TestPropertySource location not working [duplicate]

It doesn't seem that anything I do in Spring 4.1.17 with Spring Boot 1.2.6.RELEASE works at all. I just want to access the application properties and override them with test if necessary (without using the hack to inject a PropertySource manually)
this doesn't work..
#TestPropertySource(properties = {"elastic.index=test_index"})
nor does this..
#TestPropertySource(locations = "/classpath:document.properties")
nor this..
#PropertySource("classpath:/document.properties")
full test case..
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
#TestPropertySource(properties = {"elastic.index=test_index"})
public class PropertyTests {
#Value("${elastic.index}")
String index;
#Configuration
#TestPropertySource(properties = {"elastic.index=test_index"})
static class ContextConfiguration {
}
#Test
public void wtf() {
assertEquals("test_index", index);
}
}
resulting in
org.junit.ComparisonFailure:
Expected :test_index
Actual :${elastic.index}
It seems there is a lot of conflicting information between 3.x and 4.x and I can't find anything that will work for sure.
Any insight would be gratefully appreciated. Cheers!
Turns out the best way (until Spring fixes this oversight) is to a PropertySourcesPlaceholderConfigurer that will bring in test.properties (or whatever you want) and #Import or extend that #Configuration.
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
#Configuration
public class PropertyTestConfiguration {
#Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() throws IOException {
final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(ArrayUtils.addAll(
new PathMatchingResourcePatternResolver().getResources("classpath*:application.properties"),
new PathMatchingResourcePatternResolver().getResources("classpath*:test.properties")
)
);
return ppc;
}
}
This allows you to define defaults in application.properties and override them in test.properties. Of course, if you have multiple schemes, then you can configure the PropertyTestConfiguration class as necessary.
And use this in a unit test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class PropertyTests {
#Value("${elastic.index}")
String index;
#Configuration
#Import({PropertyTestConfiguration.class})
static class ContextConfiguration {
}
}
I used the locations property of #TestPropertySource to override (or add) properties.
This worked for me (spring 4.2.4):
#TestPropertySource(locations = {
"classpath:test.properties",
"classpath:test-override.properties" })
But overriding properties like below didn't:
#TestPropertySource(
locations = {"classpath:test.properties"},
properties = { "key=value" })
Even though the javadoc says that those properties have highest precedence. A bug maybe?
Update
The bug should be fixed in Spring boot version 1.4.0 and up. See the commit which closes the issue.
By now, properties declared in the presented way should get precedence.
Your use of #Value requires a PropertySourcesPlaceholderConfigurer bean to resolve ${...} placeholders. See the accepted answer here: #Value not set via Java-configured test context
If you have this problem and you're trying with yaml as properties file keep in mind that spring #TestPropertySource and #PropertySource doesn't work with yaml file, and properties won't be loaded properly.
https://github.com/spring-projects/spring-boot/issues/10772#issuecomment-339581902
For Me #TestPropertySource("classpath:xxxxxxxx.properties") worked
Have you tried using #PropertySource("classpath:document.properties") or #PropertySource("classpath*:document.properties") ?
I had issue with #TestPropertySource. test.properties not found
below is the one before fixed
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ExtContext.class)
#TestPropertySource(locations = "classpath: test.properties")
i removed space between classpath: and test.properties as below
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ExtContext.class)
#TestPropertySource(locations = "classpath:test.properties")
This worked for me, When test.properties is not found in classpth. misht work for you aswell

Specify config file for #SpringApplicationConfiguration

Currently my integration test look like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = PingFacadeApplication.class)
#WebIntegrationTest
public class PingResourceTest {
// some tests that use RestTemplate to contact PingFacadeApplication
}
PingFacadeApplication is defined like this:
#SpringBootApplication
#EnableDiscoveryClient
#ComponentScan(basePackages = "edu.self.myapp.ping")
public class PingFacadeApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "ping-facade-server");
SpringApplication.run(PingFacadeApplication.class, args);
}
}
When normally starting (i.e. running the jar) PingFacadeApplication, the config file (ping-facade-server.yml) is correctly read from src/main/resources. However, when running my integration tests, the config files appears to never be read because the server is always started on port 8080. I've also tried to put the config file in src/test/resources but no luck.
I know I can change the port in the WebIntegrationTest annotation but I'd like to avoid having it in two places.
Thanks a lot.
A solution is to indicate the name of the configuration file with the #WebIntegrationTest annotation:
// ...
#WebIntegrationTest("spring.config.name=ping-facade-server")
public class PingResourceTest {
In my case I have a ping-facade-server.yml file in src/test/resources.

Categories

Resources