I need to test my Spring application code by creating a Cucumber integration test. I am using SpringApplicationBuilder to start up my application before the actual logic is triggered and am using the following syntax to do so:-
application = new SpringApplicationBuilder()
.parent(new Object[]{"classpath:file1.xml", "classpath:file2.xml"})
.profiles("abc")
.properties("name:value") [It has 5/6 (name:value) pairs here]*
.showBanner(false)
.logStartupInfo(true)
.headless(true)
.application()
.run();
My Spring application starts up correctly. However, it does not get the values for the property (name, value) pairs that I am passing to the SpringApplicationBuilder(). I have tried the following to set them :-
Using name value pairs as above
List item using a HashMap of (name, value) pairs
Creating a ConfigurableEnvironment, retrieving the MutablePropertySources
and setting my properties in it.
None of these options are working, so when the application starts up and the code tries to access certain System Property values, it breaks.
Any ideas how this could be fixed.. All the help is greatly appreciated!
I need these properties to be Spring properties to make sure the app works correctly. Maybe I can test my code using the Spring props in some other way? If so, how do I do it?
You can configure the properties as below:
application = new SpringApplicationBuilder()
.parent(new Object[]{"classpath:file1.xml", "classpath:file2.xml"})
.profiles("abc")
.properties("key1:test1", "key2:test2")
.showBanner(false)
.logStartupInfo(true)
.headless(true)
.application()
.run();
Now, retrieve the properties using #Value annotation:
#Value("${key1}")
String val;
The val variable will be assigned the value test1
I've dealt with the same issue myself.
My problem was that the keys whose values I was trying to set were also set in an application.properties file.
After removing the entries from the file, it worked.
Related
We have a spring boot application with configuration being driven from application.yml file. In this configuration file we use the feature of defining a property by referring to another property inside the same application.yml file:
my-games-app:
base-property: foo
games:
- game-one:
game-name: ${my-games-app.base-property}one
game-location: ${my-games-app.base-property}/one
- game-two:
game-name: ${my-games-app.base-property}two
game-location: ${my-games-app.base-property}/two
And we have a #ConfigurationProperties bean loading games configuration:
#Configuration
#ConfigurationProperties(prefix = "my-games-app.games")
public class GamesConfig {
private Map<String, Game> games;
...
}
Useless to say the above is just an example, in reality it is a very complex setup with GamesConfig bean being used as a constructor argument for many other beans inside our application:
#Component
public class GamesRunner {
private final GamesConfig gamesConfig;
...
}
Everything works as expected. The problem we have is related to testing the beans where GamesConfig is injected; in the above example GamesRunner. At the moment we use #SpringBootTest to get hold of the beans we want to test. This again, works OK but the main inconvenient is that the whole application needs to be started in order to access the GamesConfig bean. This means setting up a lot of infrastructure such as a Database a JMS message broker and a Kafka broker. This takes time and makes our CI builds longer to run which started to become a bit of an inconvenient. Because the beans we want to test don't need any other setup than having the GamesConfig constructor argument provided we would prefer to have unit tests in place rather than integration tests as they are much faster to run.
In other words, we want to be able to recreate GamesConfig by hand by parsing our application.yml with a test helper method. To do this we use snakeyaml library:
public final class TestHelper {
public static GamesConfig getGamesConfig() {
var yaml = new Yaml();
var applicationYaml = (Map<String, Object>) yaml.load(readResourceAsString("application.yml");
return createGamesConfig(applicationYaml.get("games");
}
private static GamesConfig createGamesConfig(Object config) {
// The config Object passed here is a `Map<String, Map<String, String>>`
// as defeined in our `games` entry in our `application.yml`.
// The issue is that game name and game locations are loaded exactly like
// configured without property place holders being resolved
return gamesConfig;
}
}
We resolved the issue by manually parsing the property placeholders and looking up their values in the application.yml file. Even if our own property placeholder implementation is quite generic, my feeling is that this extra work is not needed as it should be a basic expectation the library would have some specific set up to do this out of the box. Being very new to snakeyaml I hope someone else hit the same problem and knows how to do it.
We use snakeyaml because it just happened to be in the class path as a transitive dependency, we are open to any suggestions that would achieve the same thing.
Thank you in advance.
To my knowledge, SnakeYAML only supports substitution of environment variables, which is why what you want is not possible as far as I know. What you can do instead, of course, is simply use Spring's classes without setting up a full ApplicationContext.
For example, assuming your game config from above, you could use:
final var loader = new YamlPropertySourceLoader();
final var sources = loader.load(
"games-config.yml",
new ClassPathResource("games-config.yml")
);
final var mutablePropertySources = new MutablePropertySources();
sources.forEach(mutablePropertySources::addFirst);
final var resolver = new PropertySourcesPropertyResolver(mutablePropertySources);
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
System.out.println(resolver.getProperty("my-games-app.games[0].game-one.game-name"));
System.out.println(resolver.getProperty("my-games-app.games[0].game-one.game-location"));
System.out.println(resolver.getProperty("my-games-app.games[1].game-two.game-name"));
System.out.println(resolver.getProperty("my-games-app.games[1].game-two.game-location"));
which outputs:
fooone
foo/one
footwo
foo/two
If you are actually interested in how Spring does it, a good starting point is the source code of the PropertySourcesPlaceholderConfigurer class.
I am using Spring Boot 2.7.5
While using #Value annotation to inject values form application.properties file, getting null assignment to variables under #Configuration class.
Example:
#Value("${file.name}")
private String fileName;
Here I am getting seeing assignment to variable fileName
Ideally it should assign value '${file.name}' if key isn't matching. But null assignment means something is breaking in the project (at least I think so, I need experts comments on this).
Same thing is working in another project but not in this particular.
Let me know if my question is not elaborative enough and will try to explain in detail
My question is, why is it working in other project but not in this one. What configurations could go wrong which I need to check. Have gone through multiple stackoverflow solutions and verified all below:
application.properties file spell check
#Configuration annotation at top of class where #Value is being used
key value pair assignment and spell check of all keys
no issues with library imports
Tried to add #PropertySource("classpath:foo.properties")
import org.springframework.beans.factory.annotation.Value;
resources folder is correctly marked as 'Resources Root'
Temporary Alternative
private Properties props = new Properties();
props.load(VaultCredential.class.getResourceAsStream("/application.properties"));
props.getProperty("file.name");
Can you please try with below code
private String PathForImageGallery;
#Value("${file.name}")
public void PathForImageGallery(String PathForImageGallery) {
this.PathForImageGallery = PathForImageGallery;
}
Apologies everyone. I was calling a class where was trying to bind #Value with properties and calling it in main() method of #SpringBootApplication.
Totally missed that main() method will not load property values until spring application is up and running.
Working solution if some one want to load properties before even running a SpringBoot application -
private Properties props = new Properties();
props.load(VaultCredential.class.getResourceAsStream("/application.properties"));
props.getProperty("file.name");
PS - Thanks all for your efforts. Admins can close it down if considered as non-logical question.
We are working on moving our application to use only Spring-Boot application.properties files. The old way we were doing was that each library/dependency would have their properties stored in a dedicated properties file like res/environment/some-library-override.properties. The values would then be retrieved in the library using #Value("$some-library-{PROPERTY_NAME}").
However, since moving all of these override properties to dedicated application.properties files, it is no longer resolving the properties and we get errors like java.lang.NumberFormatException: For input string: "$some-library-{PROPERTY_NAME}".
I assume this is because it is still expecting the property to be in that dedicated properties file.
Is there a solution to this that doesn't involve modifying the library/dependency? Is it possible to have it ignore the prefix and only look for the PROPERTY_NAME in the application.properties files?
if you have declared propertie var likeproperty.name=XXXX or added environment var like PROPERTY_NAME=XXXX.
you need to use this way
#Value("some-library-${property.name}")
// will inject value "some-library" + "XXXX"
I'm migrating from file based .properties file to consul based configuration in my spring application. I'm using spring-cloud-consul. Earlier in my property file I had a property like following
test.key=
In spring application class corresponding field is like this
#Value("${test.key:defaultVal}")
private String testConsul;
In the runtime, the value of testConsul string is an empty string. But when using the consul, whenever I put key test.key without a value, in the runtime it gets resolved to a null.
Is there anyway I can pass an empty string value through consul ?
This is the work around we're using. use a default value which is not exist
${no.such.key:}
Hope this helps.
I have a .properties file with bunch of properties in it. Here's an example:
mes.mail.debug=true
cookie.sso.domain = .stuffStuff.com
blabla.endpoint = blabla.com
test.value.property = myValue
The problem is with the last one (Which I have just added to the project we're working on).
I read the properties using #Value("${PropertyName}") annotation and it was working perfectly until lately, when I use the same thing, the variable gets the propertyName instead of its value:
#Value("${test.value.property}")
private String mProperty;
so, mProperty gets "test.value.property" where what I'm looking for is for it to get "myValue".
What's happening exactly? Is there something wrong with my project? I have tested in my friend's computer and it works perfectly.
By the way, i'm using Spring Tool Suite.
EDIT: It turns out that it doesn't detect the changes I make in the properties file. So if I change an old property's value; it acts as if nothing happened.
Does anyone has any idea why it's doing like this?
When you declare a Property Placeholder Configurer to load the properties files, you can set it to ignore unresolvable placeholders.
This means that if the property you are injecting with #Value is not found, its name (or key) will be assigned to the variable.
In your case, this option is enabled and the file that has been loaded by the application is not the one you are editing.
To see from where the file is been loaded, check the placeholder configurer location property.