I want to use a value from application.properties with the #WithUserDetails() annotation for my tests.
My current code:
#Value("${user.admin}")
private String ADMIN;
#Test
#WithUserDetails(ADMIN)
public void foo() {
}
displays the error "Attribute value must be constant"
I am using junit4 with spring runner
Seems this cannot be done, the Java compiler won't allow it as it doesn't consider the value from #Value() to be a constant at compile time.
java has built in capabilities to read a .properties file and JUnit has built in capabilities to run setup code before executing a test suite.
java reading properties:
Properties p = new Properties();
p.load(new FileReader(new File("application.properties")));
junit startup documentation
put those 2 together and you should have what you need.
But yes the minimum requirment is you have application.properties file in your test->resources package as well.
Related
I used to complete sample from Spring Data...
It works well.
I added application.properties
#spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.url=jdbc:h2:file:./h2/demo
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=wrong
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
I worked (apparently on first run it creates the database with whatever password defined).
When I changed the password to newWrong it started failing as expected (so I verified it's checking password).
Now I changed the property file to contain
spring.datasource.password=#{systemProperties['pass']}
and I changed the AccessingDataJpaApplication's main to:
public static void main(String[] args) {
System.setProperty("pass", "wrong" );
SpringApplication.run(AccessingDataJpaApplication.class);
}
and it is not working - still complaining about the password.
On the other hand, when I added
#Value("${bar}")
String foo;
and defined in application properties (and used wrong for password to prevent failing)
bar=#{systemProperties['pass']}
this statement in Application class
System.out.println("foo: " + foo);
prints foo: wrong.
Why the same is not working for spring.datasource.password property?
This might not be the exact answer to your question, but I believe it is the answer to what you are trying to solve.
You can simply pass the password to the app when you launch it on the command line by appending a -Dspring.datasource.password=wrong to the command. You can do that with any spring property.
If you are running from and IDE, you can edit the run configuration, there should be a field for VM Options where you can pass that in.
That would be the canonical way of handling in Spring.
Delete the line or change the path(example /h2/demo1).
spring.datasource.url=jdbc:h2:file:./h2/demo
because the last configuration is storage on this file.
Just tried with
spring.datasource.password=${pass}
and it works correctly.
You can also use environment variables of format SPRING_DATASOURCE_PASSWORD to define these properties. Spring Boot will resolve them without any addition to application configuration.
Ref: https://docs.spring.io/spring-boot/docs/2.2.3.RELEASE/reference/htmlsingle/#boot-features-external-config-application-property-files
Problem:
I have 3 parts in the software:
Client A service
Client B service
Target C service
I want to connect to C from A and B
I wrote a library with following setup:
/src/main/java/pkg.../TargetConnector.java
/src/main/java/pkg.../TargetConfig.java
/src/main/resources/application-dev.properties
/src/main/resources/application-tst.properties
/src/main/resources/application-prd.properties
My clients A and B both have there own sources and properties:
/src/main/java/pkg.../Client{A/B}Service.java
/src/main/java/pkg.../Client{A/B}Config.java
/src/main/resources/application-dev.properties
/src/main/resources/application-tst.properties
/src/main/resources/application-prd.properties
The properties of the Connector contains some login info for the service e.g.
target.url=https://....
target.usr=blablabla
target.key=mySHAkey
which is used in the TargetConfig to preconfigure the Connector e.g.
#Value("target.url")
String url;
#Value("target.usr")
String usr;
#Value("target.key")
String key;
#Bean
public TargetConnector connector() {
return new TargetConnector(url, usr, key);
}
Now when I use the connector jar in the client I can find the configuration via packagescan. The connector class is loaded but the problem is that it does not load the properties files.
Research
I found that multiple property files cannot have the same name (e.g. clients application-{profile}.properties clashes with the one from the connector), so I tried to rename application-{profile}.properties of the targetConnector to application-connector-{profile}.properties.
The properties whoever still do not get loaded, (which makes sense since I do not have a e.g connector-dev profile but my profile is simply named dev).
Furthermore, even if I try to explicitly load one of the property files from the connector with:
#PropertySource({"classpath*:application-connector-dev.properties"})
it cannot be found
Question
My question is actually 3 tiered:
How can I load a property file in a dependency jar at all?
How can I load the profiled version of the property file if the the properties file has a different name than application.properties? e.g. application-connector.properties
How can i combine the answers from question 1 and 2 to load the profiled version of the property in the jar?
If further explanation is needed, please ask.
Answer
I went for an approach as given in the accepted answer.
I Just created 3 configs for the dev, tst, prd profiles containing the values needed and annotated the config files with the correct profiles.
You are using #Configuration annotated class. Maybe you can have one per profile. Here you are an example:
#Configuration
#Profile("profileA")
#PropertySource({"classpath:application-profileA.properties"})
public class ConfigurationProfileA{
#Value("${target.url}")
String url;
#Value("${target.usr}")
String usr;
#Value("${target.key}")
String key;
#Bean
public TargetConnector connector() {
return new TargetConnector(url, usr, key);
}
}
Do the same for profile B (maybe you can structure this better but the key points here are the annotation #Profile("") and #PropertySource(""))
Once you have your config class, Spring will use the Configuration class you want by just filling -spring.profiles.active=profileA (or the name of the profile you have written in the #Profile("") annotation)
I think there is a typo in this line #PropertySource({"classpath*:application-connector-dev.properties"})
Please check by removing the asterik.
In order to run with a specific profile, you can run with option -spring.profiles.active=dev for example
If you don’t run with a profile, it will load the default profile in application.properties that you don’t seem to have.
Furthermore, an advice would be to always have an application.properties and put in it the common properties and the default values that you would override in other properties files.
Other mistake is how you assign properties with #Value annotation, you need to use #Value("${PROPERTY_FROM_PROPERTIES_FILE}")
I have written a cucumber integration tests and it is running ok.
And then i wanted some class variables from the step.java to get their values from .properties values
public class cucumberStepClass {
#Value("${value.from.propertiesfile}")
private String variable
//rest of integration test
}
Does anyone know how can i inject those values so my test can use them?
Have you enabled integration with spring dependency injection? You need to add the cucumber-spring dependency for that. See https://docs.cucumber.io/cucumber/state/#spring
To add my two cents to the first answer: remember to annotate the class with #SpringBootTest and #TestPropertySource("classpath:test.properties"). And the properties file should be .properties extension or .xml. .yml cannot be loaded.
Using Spring Boot 1.3.0.RELEASE
I have a couple of yaml files that describe several instances of a program. I now want to parse all those files into a List<Program> (Map, whatever), so I can later on search for the most appropriate instance for a given criteria in all the programs.
I like the approach with #ConfigurationProperties a lot, and it works good enough for a single yaml-file, but I haven't found a way yet to read all files in a directory using that method.
Current approach working for a single file:
programs/program1.yml
name: Program 1
minDays: 4
maxDays: 6
can be read by
#Configuration
#ConfigurationProperties(locations = "classpath:programs/program1.yml", ignoreUnknownFields = false)
public class ProgramProperties {
private Program test; //Program is a POJO with all the fields in the yml.
//getters+setters
I tried changing the locations to an Array listing all of my files locations = {"classpath:programs/program1.yml", "classpath:programs/program2.yml"} as well as using locations = "classpath:programs/*.yml", but that still only loads the first file (array-approach) or nothing at all (wildcard-approach).
So, my question is, what is the best way in Spring Boot to load a bunch of yaml files in a classpath-directory and parse them into a (List of) POJO, so they can be autowired in a Controller? Do I need to use Snakeyaml directly, or is there an integrated mechanism that I just haven't found yet?
EDIT:
A working approach is doing it manually:
private static final Yaml yaml = new Yaml(new Constructor(Program.class));
private static final ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
for (Resource resource : resolver.getResources("/programs/*.yml")) {
Object data = yaml.load(resource.getInputStream());
programList.add((Program) data);
}
}
catch (IOException ioe) {
logger.error("failed to load resource", ioe);
}
In Spring, it is possible to load multiple configuration properties files using PropertySource annotation, but not YAML files. See section 26.6.4 in link below:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
However, from your problem, it seems that you can configure all your programs in single YAML and then get all list of programs in a single list.
Sample YAML (all.yaml)
programs:
- name: A
min: 1
max: 2
- name: B
min: 3
max: 4
Config.java
#Configuration
#ConfigurationProperties(locations={"classpath:all.yaml"})
public class Config{
private List<Program> programs;
public void setPrograms(List<Program> programs) {
this.programs = programs;
}
public List<Program> getPrograms() {
return programs;
}
}
What I am currently doing, as far as I understood your question, is nearly the same.
I am having an application.yml and also profile-specific yml files, e.g. application-{profile}.yml in my src/main/resources.
In the application.yml I have defined the default profile key-values, which are partially overridden by the profile-specific yml files.
If you want to have a type-safe and well defined access of your YML key/values, then you can use the following approach:
#ConfigurationProperties
public class AppSettings {
String name; // has to be the same as the key in your yml file
// setters/getters
}
In your Spring-Boot config, you have to add the following annotations onto your config class:
#ComponentScan
#EnableAutoConfiguration
#EnableConfigurationProperties( value = { AppSettings.class, SomeOtherSettings.class } )
public class SpringContextConfig {
#Autowired
private AppSettings appSettings;
public void test() {
System.out.println(appSettings.getName());
}
}
The #Autowiring is also accessible from other Beans.
The other way around (without an extra separated and type-safe class, is to access the YML-values via #Value("${name}").
To bring it together in a short manner:
Yes, it is possible to use several YAML files for your application via Spring-profiles. You define your current active spring profile via command args, programmatically or via your system env (SPRING_PROFILES_ACTIVE=name1,name2).
Therefore you can have several application.yml files for each profile (see above).
I have two different environments that need different property values and have researched that the proper way to do this is using spring profiles. As such I have setup two different files:
application.properties
application-dev.properties
The run-time environment is setup and includes the following:
spring.profiles.active=dev
Inside the application.properties file a property "foo.bar" set where:
foo.bar=defaultProp
in the applications-dev.properties file
foo.bar=devProp
Inside the app the following code is included:
#Value("${foo.bar}")
String foobar;
#Autowired
Environment env;
When I run the app with the following:
String x = env.getProperty("spring.profiles.active");
x reports the value of "dev" (working as expected)
BUT
foobar reports its value as "defaultProp"
Both the applications.properties and applications-dev.properties are located together in the same directory.
Later the following was included for testing:
for(String s: env.getActiveProfiles())
{
logger.info("Act:" + s);
}
for(String s: env.getDefaultProfiles())
{
logger.info("Def:" + s);
}
with the following output:
Act:dev
Def:default
Again this was also expected and appears to be working
Finally, another variable was inserted into application-dev.properties
ding=dong
and in the app the following code was wired in
#Value("${ding}")
String dingvalue;
Low and behold the value of "dingvalue" reports as "dong", again this is working as expected -- values are being picked up from the application-dev.properties file!
Hence it appears that the active profile is actually being set to "dev" and values are being picked up as expected.
Finally, according the doc.spring.io:
23.4 Profile-specific properties
In addition to application.properties files, profile-specific
properties can also be defined using the naming convention
application-{profile}.properties.
Profile specific properties are loaded from the same locations as
standard application.properties, with profile-specific files always
overriding the default ones irrespective of whether the
profile-specific files are inside or outside your packaged jar.
Can anyone please explain why the application.properties value is not being overwritten by the application-dev.properties as per the documentation?
It is working as expected.
/product-model-configuration/src/main/resources/product-model.yml:
foo:
bar: defaultProp
/product-model-configuration/src/main/resources/product-model-loc-isolated.yml:
foo:
bar: bar