Spring jUnit Testing properties file - java

I have a jUnit Test that has its own properties file(application-test.properties) and its spring config file(application-core-test.xml).
One of the method uses an object instantiated by spring config and that is a spring component. One of the members in the classes derives its value from application.properties which is our main properties file. While accessing this value through jUnit it is always null. I even tried changing the properties file to point to the actual properties file, but that doesnt seem to work.
Here is how I am accessing the properties file object
#Component
#PropertySource("classpath:application.properties")
public abstract class A {
#Value("${test.value}")
public String value;
public A(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
public A(String text) {
this();
// do something with text and value.. here is where I run into NPE
}
}
public class B extends A {
//addtnl code
private B() {
}
private B(String text) {
super(text)
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:META-INF/spring/application-core-test.xml",
"classpath:META-INF/spring/application-schedule-test.xml"})
#PropertySource("classpath:application-test.properties")
public class TestD {
#Value("${value.works}")
public String valueWorks;
#Test
public void testBlah() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
B b= new B("blah");
//...addtnl code
}
}

Firstly, application.properties in the #PropertySource should read application-test.properties if that's what the file is named (matching these things up matters):
#PropertySource("classpath:application-test.properties ")
That file should be under your /src/test/resources classpath (at the root).
I don't understand why you'd specify a dependency hard coded to a file called application-test.properties. Is that component only to be used in the test environment?
The normal thing to do is to have property files with the same name on different classpaths. You load one or the other depending on whether you are running your tests or not.
In a typically laid out application, you'd have:
src/test/resources/application.properties
and
src/main/resources/application.properties
And then inject it like this:
#PropertySource("classpath:application.properties")
The even better thing to do would be to expose that property file as a bean in your spring context and then inject that bean into any component that needs it. This way your code is not littered with references to application.properties and you can use anything you want as a source of properties. Here's an example: how to read properties file in spring project?

As for the testing, you should use from Spring 4.1 which will overwrite the properties defined in other places:
#TestPropertySource("classpath:application-test.properties")
Test property sources have higher precedence than those loaded from the operating system's environment or Java system properties as well as property sources added by the application like #PropertySource

I faced the same issue, spent too much calories searching for the right fix until I decided to settle down with file reading:
Properties configProps = new Properties();
InputStream iStream = new ClassPathResource("myapp-test.properties").getInputStream();
InputStream iStream = getConfigFile();
configProps.load(iStream);

If you are using a jar file inside a docker container, and the resource properties file, say application.properties is packaged within the same classes directory that contains the java(this is what IntelliJ IDE does automatically for resources file stored in /src/main/resources), this is what helped me:
public static Properties props = new Properties();
static {
try {
props.load(
Thread
.currentThread()
.getContextClassLoader()
.getResourceAsStream("application.properties")
);
} catch (Exception e) {
e.printStackTrace();
}
}
Most other methods either only worked inside the IDE or inside the Docker. This one works in both.

if you want to load a few properties, I found a good way in the spring
ReflectionTestUtils.
#Before
Public void setup(){
ReflectionTestUtils.setField(youclassobject, "value", "yourValue")
}
>you can follow this link as well for more details https://roytuts.com/mock-an- autowired-value-field-in-spring-with-junit-mockito/

Related

Getting a SpringBoot property in a non-Spring class

I've got a number of properties defined in my application.properties file. These are loaded into a number of different configuration files across the system via #Configuration, #PropertySource, and #ConfigurationProperties annotations.
In addition, I have a library, separate from this system, that has no dependence on Spring (and ideally will stay that way). At some point in the execution of the system, it initializes an instance of a class from this library via reflection and a no args constructor. However, in that initialization, I want to get a Spring property and assign it to a local field - however, as I mentioned before, this library class is not spring configured, and is in fact in an entirely different project. How can this be done?
My current solution is that when the property is initialized in the config class, the setter for the property also sets a system property (via System.setProperty("someProp", propValue)), and then in the no args constructor of the library class I call System.getProperty("someProp"). However, this feels really hacky, particularly the part where I set the variable. Perhaps there is some way to configure SpringBoot to automatically propagate that particular property up to become a System property as well?
My code atm
ServiceConfig.class
#Configuration
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "service")
public class ServiceConfig {
private String serviceUrl;
public String getServiceUrl() {
return serviceUrl;
}
public void setServiceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
System.setProperty("SERVICE_URL", serviceUrl);
}
}
My application.properties
service.serviceUrl=http://localhost:8000
LibraryClass.class
public class LibraryClass {
private final String serviceUrl;
public LibraryClass() {
this.serviceUrl = getProperty("OAUTH_SERVICE_URL");
}
...
}
if your LibraryClass has a setter method - in the example below named setServiceUrl(...) - to set this configuration property you could add this to your existing ServiceConfig configuration class:
#Bean
public LibraryClass getLibraryClass(#Value(${"OAUTH_SERVICE_URL"}) String serviceUrl) {
LibraryClass libraryClass = new LibraryClass();
libraryClass.setServiceUrl(serviceUrl);
return libraryClass;
}
Other than that - if you cannot modify LibraryClass because it's parts of a 3rd party library or so... you could use Spring's Environment instance, to read all needed properties that - later - will be accessed inside of the constructor of LibraryClass and set them just the way you did as System properties. Also add this to your config class:
#Autowired
public void setSystemPropsNeededForLibraryClassConstruction(Environment environment) {
System.setProperty("serviceUrl", environent.getProperty("serviceUrl"));
}

Where is getResourceAsStream("file") searching when running from a test?

I'm struggling to create a test to verify a ServletListener that loads a properties file. I've tested when running the application that it works fine and it finds the file in the classpath. But I don't know how to create a test for it. My idea is to create a temp file with a test property and then verify the property is put into the System properties. But I always fail to create the file in the right place.
I've tried creating the file in /target/test-classes or directly in the root of the application but it never finds it. Any idea?
This is the code I'm trying to test:
public class PropertyReadingListener implements ServletContextListener {
public static final String PROFILES_PROPERTIES = "profiles.properties";
#Override
public void contextDestroyed(ServletContextEvent event) {
}
#Override
public void contextInitialized(ServletContextEvent event) {
Properties propsFromFile = new Properties();
try {
propsFromFile.load(getClass().getResourceAsStream(PROFILES_PROPERTIES));
} catch (final Exception e) {
log.warn("Unable to find {}, using default profile", PROFILES_PROPERTIES);
}
propsFromFile.stringPropertyNames().stream()
.filter(prop -> System.getProperty(prop) == null)
.forEach(prop -> System.setProperty(prop, propsFromFile.getProperty(prop)));
}
}
Thanks.
Assuming that you are using maven, put your properties file here:
src/test/resources/foo.properties
The maven resources plugin will place the (possibly filtered) copy of the file in
target/test-classes/foo.properties
The test-classes directory is on the classpath, so you reference the file like this (note the slash in front of the file name):
... getResourceAsStream("/foo.properties");
Where is getResourceAsStream("file") searching when running from a test?
Assuming that you are talking about JUnit ....
Unless you have done something funky, your unit tests will be loaded by the default classloader, and that means that the normal JVM classpath with be searched.
(Junit 4 allows you to use a different classloader: see https://stackoverflow.com/a/9192126/139985)
But I always fail to create the file in the right place.
It seems that your real problem is understanding how the Java classpath and classpath searching works. If you understand that (and you know what JUnit runner's actual classpath is) then it should be obvious where to put the properties file so that the classloader can find it.
See Different ways of loading a file as an InputStream
Basically when you do a getClass().getResourceAsStream it looks in the package of that class for the file.. so if your PropertyReadingListener is in com.company.listeners.PropertyReadingListener then it will look in com/company/listeners for the property file.
For testability, I would pass in an InputStream into the listener that way the test can create the input stream in a convienent way and the actual user of the class in code can pass in the InputStream returned from getResourceAsStream

How can I mock the presence of a properties file on the classpath?

This surely is a common problem. I have a properties file like my-settings.properties which is read by an application class. When I write a test class, it needs to test different scenarios of things that could be present in my-settings.properties in order to ensure maximum code coverage (e.g. empty properties file, basic properties file etc). But I can only have one my-settings.properties in my src/test/resources.
What would be really great is if there was just some annotation
#MockFileOnClassPath(use = "my-settings-basic.properties", insteadOf = "my-settings.properties")
Then I could just have multiple my-settings-XXX.properties files in my /src/test/resources and just annotated the correct one on each test method. But I can't find anything like this. I'm using JUnit 4.12.
I can think of a couple of crude solutions:
Before each test, find the file on the file system, copy it using filesystem I/O, then delete it again after the test. But this is clumsy and involves a lot of redundancy. Not to mention I'm not even sure whether the classpath directory will be writable.
Use a mocking framework to mock getResource. No idea how I would even do that, especially as there are a million different ways to get the file (this.getClass().getResourceAsStream(...), MyClass.class.getResourceAsStream(...), ClassLoader.getSystemClassLoader().getResourceAsStream(...) etc.)
I just think this must be a common problem and maybe there is already a solution in JUnit, Mockito, PowerMock, EasyMock or something like that?
EDIT: Someone has specified that this question is a duplicate of Specifying a custom log4j.properties file for all of JUnit tests run from Eclipse but it isn't. That question is about wanting to have a different properties file between the main and test invocations. For me I want to have a different properties file between a test invocation and another test invocation.
I find that whenever dealing with files, it's best to introduce the concept of a Resource.
eg:
public interface Resource {
String getName();
InputStream getStream();
}
Then you can pass the resource in via dependency injection:
public class MyService {
private final Properties properties;
public class MyService(Resource propFile) {
this.properties = new Properties();
this.properties.load(propFile.getStream());
}
...
}
Then, in your production code you can use a ClasspathResource or maybe a FileResource or URLResource etc but in your tests you could have a StringResource etc.
Note, if you use spring you already have an implenentation of this concept. More details here
You can change your Service class to accept the name of the resource file, then then use that name to load the resource.
public class MyService {
public MyService(String resourceFileName){
//and load it into Properties getResourceAsStream(resourceFileName);
}
}

How can I get my config file from class to class?

Just a quick and simple question. I have a program with several classes that read information off of a .properties file. Is it better practice to pass the file from class to class as an argument in the constructor, or open the file directly in each class?
If you're going to do this by hand, I would recommend you create a configuration class, that takes the file via the constructor, and reads the property values into member variables. Then every other class that needs configuration takes a Configuration class via it's constructor. However, almost no one does this, and instead uses a framework like spring, which handles property injection for you.
In spring, it would look something like this:
<!-- application context xml file -->
<context:property-placeholder location="file:///some/path/to/file" />
Then in your java classes:
public class SomeClass {
#Value("${some.property}")
private String someProp;
#Value("${some.other.prop}")
private Integer someOtherProp;
// ...
}
At application startup the properties get injected into your class.
My suggestion is to have a Util class which loads the properties file and get values from that Util to the required classes.
Note: I dont think you have any issues on loading the properties file.
I would suggest that you create an immutable class that takes in the file as a constructor argument and sets all the instance variables. I'd call it PropertyConfiguration. Then since the class is immutable, you won't have to worry about passing it to everyone. You could even have a class that holds it.
For example, the code below would set you up to have a nice set up to have several things available project wide. I would just ensure that anything that's shared be immutable to ensure thread safety.
public class ClientUtils {
private static ClientContext _clientContext = null;
public static void setClientContext(ClientContext cc) {
_clientContext = cc;
}
public static ClientContext getContext() {
return _clientContext;
}
}
public class ClientContext {
private final Configuration _configuration;
public ClientContext(Configuration config){
_configuration = config;
}
public Configuration getClientContext() {
return _configuration;
}
}
If your program contains data which need not be a part of compilation and can vary from deployment to deployment, you must add it to the properties file : ( Things like database connection string, email addresses ).
Just in case you need this, I'm adding the code for accessing the properties files.
Drop the file in build directory.
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("credentials.properties"));

properties at application level

I am currently having a properties file and I am loading this file in each class where there is a need to get the properties
static PropertiesConfiguration config = null;
config = new PropertiesConfiguration("Interface.properties");
This is working fine. But I know this is not the efficient way to load properties file multiple times.
Can anyone help me how to create the properties file at application level and create a java file (say config.java) + calling the method which returns the property.
One way to solve this is to utilize the dependency injection. You can have a singleton bean that holds the property instance and then you inject this bean as a dependency to each of your class that needs to access those properties.
One way to implement dependency injection to use the Spring framework. For instance, you can achieve loading the property file using Spring's PropertyPlaceHolderConfigurer. Then this bean becomes your reference bean for rest of the application. A tutorial on this can be found here.
Another choice for you is to load the property file in the constructor of the main entry class or the main method of your application and then pass the object to the classes that needs it. However, this will make your application more tightly coupled and maintenance would be harder in the future.
Another option is to create a singleton class that loads the properties and have a method that returns the values as needed.
I used this approach to set the properties at application level.
Define a properties file (say configure.properties).
Create a java class Config:
public class Config {
private static Config instance;
private PropertiesConfiguration configure;
private Config() throws ConfigurationException {
configure = new PropertiesConfiguration("configure.properties");
}
public static Config getInstance() {
if (null == instance) {
try {
instance = new Config();
} catch (ConfigurationException ex) {
throw new RuntimeException(ex);
}
}
return instance;
}
public PropertiesConfiguration getConfigure() {
return configure;
}
public void setConfig(PropertiesConfiguration configure) {
this.configure = configure;
}
}
This java class load the properties file at the startup and calls the getInstance method to get the value of the property. To get the value of the property anywhere else in the application import Config and
Config.getInstance().getConfig().getString("property.given.in.properties");
Sorry if variable names doesn't make much sense.
Thanks all for your input.
I just load my properties file in the System Properties:
public void loadStreamToSysProperties(InputStream in) throws IOException
{
Properties p = System.getProperties();
p.load(in);
}
Then I can get them out of System.getProperty where ever I am in the code.
In this example the InputStream is a Stream I created from the file name/path.

Categories