Spring Boot Test: #TestPropertySource not overriding #EnableAutoConfiguration - java

I am using Spring Data LDAP to get user data from an LDAP server.
My file structure looks like this:
main
java
com.test.ldap
Application.java
Person.java
PersonRepository.java
resources
application.yml
schema.ldif
test
java
Tests.java
resources
test.yml
test_schema.ldif
And here is my test class:
import com.test.ldap.Person;
import com.test.ldap.PersonRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {PersonRepository.class})
#TestPropertySource(locations = "classpath:test.yml")
#EnableAutoConfiguration
public class Tests {
#Autowired
private PersonRepository personRepository;
#Test
public void testGetPersonByLastName() {
List<Person> names = personRepository.getPersonNamesByLastName("Bachman");
assert(names.size() > 0);
}
}
The problem is, Spring Boot is loading the application.yml and schema.ldif files instead of my test YAML and LDIF files, despite the fact that my #TestPropertySource annotation is explicitly listing test.yml. This seems to be due to the auto configuration, which I would prefer to use for convenience.
I would expect #TestPropertySource to take higher precedence than the auto configuration, but that does not seem to be the case. Is this a bug in Spring, or am I misunderstanding something?
For the record, here is my test.yml file (it does specify test_schema.ldif):
spring:
ldap:
# Embedded Spring LDAP
embedded:
base-dn: dc=test,dc=com
credential:
username: uid=admin
password: secret
ldif: classpath:test_schema.ldif
port: 12345
validation:
enabled: false

So I was able to work around this by manually specifying the properties needed to make use of the LDIF file. This is because, according to the #TestPropertySource documentation, inlined properties have higher preferences than property files.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {PersonRepository.class})
#TestPropertySource(properties =
{"spring.ldap.embedded.ldif=test_schema.ldif", "spring.ldap.embedded.base-dn=dc=test,dc=com"})
#EnableAutoConfiguration
public class Tests {
//...
}
This is not the best workaround, however: what if I had more than just two properties I needed to define? It would be impractical to list them all there.
Edit:
Renaming my test.yml file to application.yml so it overrides the production file that way did the trick. As it turns out, the TestPropertySource annotation only works for .properties files.

I discovered that YML files DO NOT work with #TestPropertySource annotation.
A clean way around this is to use #ActiveProfile. Assuming that your YML file with test properties is called
application-integration-test.yml
then you should use the annotation like this
#ActiveProfile("integration-test")

Related

Spring override non-application.properties file in Tests

I use an external pure Java library that uses a file that is called updater.properties.
Im currently writing some Spring-Boot Integration Tests with #SpringBootTest.
The problem I currently have is that i don’t find any option to pass/use a different updater.properties file with Spring. Is there a solution for this? Or does the library need to be updated for this to work?
The updater.properties is in my resource folder and being parsed internally by the library.
Would be happy for any help!
#SaltukKezer, yes you can. First step make a config class like
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:/updater.properties")
public class UpdaterProperties{
#Autowired
Environment env;
public String getMyPropertyValue() {
return env.getProperty("your.property.name");
}
}
Then second step, just get an instance of this class and call the getters

Java Spring bean creation and access from external jar

I have spring jar for my entity beans created as export--java--JARfile, where i have beans defined as follows:
package com.my.beans;
#Component("expBean")
public class ExpBean {
}
I also have config in this jar
package com.my;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
#Bean
public ExpBean getExpBean() {
return new ExpBean();
}
}
I have client spring application where i am adding above jar having only beans as by external dependency and trying to get my beans when spring app starts using following code in main code in client spring app.
package com.my;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication()
#ComponentScan("com.my")
public class Application {
public static void main (String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
WMyBean bean = (WMyBean) ctx.getBean("expBean");
bean.doSomething();
}
}
But when checked the list of bean definition printed, i do not see my bean from external jar and also i get following error.
"Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'expBean' available"
I tried many options but not sure whats missing.
Option 1
#ComponentScan("com.my")
#SpringBootApplication
public class Application {
Option 2
#ComponentScan({"com.my"})
#SpringBootApplication
public class Application {
Need valuable inputs on step that i am missing out.
Question 1: Is it mandatory for me to do config change in client project to access my dependent jar entities?
Question 2: Can it be dynamic, load all entities without configuration as #bean in client project config?
Question 3: Do i need to build client jar every-time i change my dependent jar file with more entities?
Thanks
First I am assuming the build tool is Gradle, but you can import the jar as a local file like this for the sake of experiment:
dependencies {
implementation files('libs/something_local.jar')}
Then, assuming you use Eclipse you can right click the project folder and do a 'Gradle refresh'
In your config class you can either autowire in the bean for the class as a whole , or choose to create it whenever an instance of the class is created by declaring it within the class constructor:
import org.springframework.beans.factory.annotation.Autowired;
//I also have config in this jar
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
//Autowire in the bean for the whole class.
#Autowired
ExpBean expBean;
public pBeanConfig() {
//Or, you may want to have a bean instance whenever the pBeanConfig class is instantiated.
ExBean exBean;
}
}
I'm making a number of assumptions for this answer like it's a Gradle project, but you can set up a similar dependency for Maven by modifying the POM file to include that file.
Build tools aside, in Eclipse you can simply add the jar file by right clicking the project and build path => configure build path => add external jar file.
UPDATE:
This pic describes how I set up the bean within a separate jar file via eclipse. The steps should show that the bean can be reached with this project even though it's in a jar and not in the project itself.

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

FindBugs null issue with Spring #Autowired in Eclipse

I am using the findbugs Eclipse plugin (3.0.1.20150306-5afe4d1), spring (4.2.2.RELEASE), and eclipse (Mars.1 (4.5.1)) together and I am receiving the following FindBugs bug in Eclipse.
Non-null field env is not initialized by new
org.test.app.config.AppConfiguration() [Scary(8), Normal confidence]
I am using using the default constructor and using autowiring to initialize the env variable. I also have a PostConstruct annotation which gets called after everything is wired and accesses the env variable to make sure it was initialized correctly.
How can I make this error disappear without turning off the FindBugs plugin and still using the #Autowired annotation?
package org.test.app.config;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#ComponentScan(basePackages = { "org.test.app" })
#PropertySource("classpath:/${spring.profiles.active:local}.properties")
public class AppConfiguration {
private static final Logger log = LoggerFactory.getLogger(AppConfiguration.class);
#Autowired
private Environment env;
/**
* Dump profile info.
*/
#PostConstruct
public void details() {
log.debug("** App application context, active profile(s)={}", Arrays.toString(env.getActiveProfiles()));
}
}
Update
I tried using a constructor per #spoonybard896 suggestion, but it did not work. I received the following error.
java.lang.NoSuchMethodException: org.test.app.config. AppConfiguration $$EnhancerBySpringCGLIB$$cbece1d7.<init>()
[STDOUT] at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_60]
[STDOUT] at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_60]
[STDOUT] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiat‌​e(SimpleInstantiationStrategy.java:80) ~[na:na]
How about using a non-default constructor instead?
private final Environment env;
#Autowired
public AppConfiguration(final Environment env) {
this.env = env;
}
EDIT
The above approach would work for a #Controller instance, but will not work for #Configuration. After doing some quick research it turns out that:
#Configuration is meta-annotated with #Component, therefore #Configuration classes are candidates for component scanning (typically using Spring XML's <context:component-scan/> element) and therefore may also take advantage of #Autowired/#Inject at the field and method level (but not at the constructor level).
I'm thinking that unless there is some kind of addon for FindBugs that understands Spring annotations (I do not know of one) then you may just need to apply a filter to the FindBugs plugin and have it ignore that particular error in that particular file(or in any Configuration class in general). In Eclipse check out Preferences -> Java -> Findbugs -> Filter Files, and check out this link which describes a similar problem and resolution, but just make sure to filter out only the error you want. The goal would not be turn FindBugs off, but instead have it just ignore this one case.
EDIT 2
Adding an annotation to the class will suppress the FindBugs error for this file only.
#SuppressFBWarnings(
value="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
justification="Overriding the check on the env variable because Spring will automatically initialize the variable after the constructor is called and before any public methods are called.")

How it works together these 2 Spring Java configuration classes?

I am studying for the Spring Core certification and I have the followind doubt with an exercice related to the beans configuration using the Java configuration way.
So I have the following RewardsConfig class that configure my beans (this class is into the application folder src/main/java):
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
#Configuration
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(accountRepository(), restaurantRepository(), rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see I declare 4 methods that are used to create 4 beans and that specify the dependency that occurs among these beans.
So I have a RewardNetwork bean that is implemented by RewardNetworkImpl class that depends from the following 3 beans: AccountRepository, RestaurantRepository and RewardRepository.
Is it the correct interpretation of the Java configuration is Spring?
Can I say for example that RewardNetwork is the declared bean and that RewardNetworkImpl its the current implementation of this bean?
All the 3beans (AccountRepository, RestaurantRepository and RewardRepository) depends by another bean dataSource that, as you can see in the previous code snippet, is declared as #Autowired:
#Autowired
DataSource dataSource;
This bean is not declared in this configuration class because it changes according to the environment (test, developt, production).
So, in my case it is declared into the unit test folder src/test/java:
package rewards;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
#Configuration
public class TestInfrastructureConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
So the dataSource bean define a datasource that is valid only for the test environment (used when I perform a unit test).
Now my doubt is: I have 2 different configuration classes and the dataSource bean is not definied into the RewardsConfig configuration class that contains the 3 beans that use it. Why I can't not use the #Import annotation to use it into RewardsConfig?
Something like it:
#Import(TestInfrastructureConfig.class)
How it work exactly?
Tnx
You don't have to import beans to make them available for autowiring. #Import is used to add extra configuration classes.
You really don't want to hard-import a test configuration class, because then your production code is referring to test-only code (and, in this case, always activating it). Instead, think of your configuration class more like an abstract class: declare autowired beans, but don't worry about how they get there. The downstream (runtime) configuration will supply them, and you don't need to know how. Maybe you're supplying an in-memory H2 for testing and using Spring Cloud Connectors for actual runs, doesn't matter.

Categories

Resources