Spring Boot integration tests doesn't read properties files - java

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.

Related

Springboot loading wrong config despite being explicit

I have the following Configuration classes, one in the main package and one in the test package.
Main
#Configuration
public class DynamoConfiguration {
Test
#TestConfiguration
public class DynamoTestConfiguration {
Unit Test
#ActiveProfiles(profiles = "test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
Yet, it's still loading DynamoConfiguration and causing failures when I only want the DynamoTestConfiguration to be loaded. How can I ensure that happens?
When using #SpringBootTest, then your application is started, along with any #Configuration classes on the classpath. Spring has no idea that DynamoConfiguration is special and you don't want to load it.
As a way around this, you can use profiles:
#Profile("prod")
#Configuration
public class DynamoConfiguration {
and in your test, add !prod to your #ActiveProfiles:
#ActiveProfiles(profiles = "!prod,test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
This should avoid that DynamoConfiguration gets loaded in the test.

Testing Spring Boot Library Modules

I got a multi module project where not every module is actually an application but a lot of them are libs. Those libs are doing the major work and I want to test them where they are implemented. The current dependencies of the libs:
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
In the main source is a class with #Configuration and a single bean:
#Bean public String testString() { return "A Test String"; }
I got 2 test classes:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test1 {
#Test
public void conextLoaded() {
}
}
-
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test2 {
#Autowired
private String testString;
#Test
public void conextLoaded() {
}
}
The first test works. The second does not. There is not #SpringBootApplication anywhere in that project so in the same package as the Tests I added a test configuration:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan("com.to.config")
public class LibTestConfiguration {
}
And it does not work. Same for classes that are #Service. They are not in the context. How can I make it behave like a normal Spring boot application without it actually beeing one and load the configs and contexts from the configurations files I need? The default and test profile share most of their properties (for now) and I want them to be loaded like I would start a tomcat.
I switched to JUnit 5 and made it kinda work... So if you want to test Database stuff:
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
class BasicMongoTest { ... }
Lets you autowire all repositories and mongo template
Initializes with apllicaton.yml config
Does NOT initialize or configure interceptors
Full application context test if you have a class with #SpringBootApplication in your classpath (Can be an empty test main in your test context)
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class FullContextTest { ... }
Initializes the full context with all configs and beans
Should not be done if not necessary as it loads all the application context and kinda defeats the purpose of unit tests to only activate whats needed.
Test only specific components and configs:
#SpringBootTest(classes = {Config1.class, Component1.class})
#EnableConfigurationProperties
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class SpecificComponentsTest { ... }
Initializes the context with only the Config1 and Component1 classes. Component1 and all beans in Config1 can be autowired.

Create Spring Boot test with separate application.properties

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 {
...
}

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

Spring boot test configuration not being picked

I am writing an integration test for my application, and want to use a custom webmvc configuration for my tests
I have three classes in my base package com.marco.nutri:
Application(which is annotated with #SpringBootApplication)
MvcConfig(#Configuration and #EnableWebMVC)
SecurityConfig(#Configuration and #EnableWebSecurity)
My test is in the package br.com.marco.nutri.integration.auth:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={Application.class, WebMvcTestConfiguration.class, SecurityConfig.class})
public class ITSignup {
//Test code
}
I have a test config class in the package com.marco.nutri.integration:
#TestConfiguration
#EnableWebMvc
public class WebMvcTestConfiguration extends WebMvcConfigurerAdapter {
//Some configuration
}
But when I run my test, the MvcConfig.class is picked instead of WebMvcTestConfiguration.class
What am I doing wrong?
you can annotate your test configuration with #Profile("test") and your real one with #Profile("production")
and in your properties file put the property spring.profiles.active=production and in your test class put #Profile("test"). So when your application starts it will use "production" class and when test stars it will use "test" class.
from documentation
Unlike regular #Configuration classes the use of #TestConfiguration
does not prevent auto-detection of #SpringBootConfiguration.
Unlike a nested #Configuration class which would be used instead of a
your application’s primary configuration, a nested #TestConfiguration
class will be used in addition to your application’s primary
configuration.

Categories

Resources