Does JUnit support properties files for tests? - java

I have JUnit tests that need to run in various different staging environments. Each of the environments have different login credentials or other aspects that are specific to that environment. My plan is to pass an environment variable into the VM to indicate which environment to use. Then use that var to read from a properties file.
Does JUnit have any build in capabilities to read a .properties file?

It is usually preferred to use class path relative files for unit test properties, so they can run without worrying about file paths. The path may be different on your dev box, or the build server, or where ever. This will also work from ant, maven, eclipse without changes.
private Properties props = new Properties();
InputStream is = ClassLoader.getSystemResourceAsStream("unittest.properties");
try {
props.load(is);
}
catch (IOException e) {
// Handle exception here
}
putting the "unittest.properties" file at the root of the classpath.

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("config.properties")));
junit startup documentation
put those 2 together and you should have what you need.

//
// Load properties to control unit test behaviour.
// Add code in setUp() method or any #Before method (JUnit4).
//
// Corrected previous example: - Properties.load() takes an InputStream type.
//
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
Properties p = new Properties();
p.load(new FileInputStream( new File("unittest.properties")));
// loading properties in XML format
Properties pXML = new Properties();
pXML.loadFromXML(new FileInputStream( new File("unittest.xml")));

This answer is intended to help those who use Maven.
I also prefer to use the local classloader and close my resources.
Create your test properties file, called /project/src/test/resources/your.properties
If you use an IDE, you may need to mark /src/test/resources as a "Test Resources root"
add some code:
// inside a YourTestClass test method
try (InputStream is = loadFile("your.properties")) {
p.load(new InputStreamReader(is));
}
// a helper method; you can put this in a utility class if you use it often
// utility to expose file resource
private static InputStream loadFile(String path) {
return YourTestClass.class.getClassLoader().getResourceAsStream(path);
}

If the aim is to load a .properties file into System Properties, then System Stubs (https://github.com/webcompere/system-stubs) can help:
The SystemProperties object, which can be used either as a JUnit 4 rule to apply it within a test method, or as part of the JUnit 5 plugin, allows setting properties from a properties file:
SystemProperties props = new SystemProperties()
.set(fromFile("src/test/resources/test.properties"));
The SystemProperties object then needs to be made active. This is achieved either by marking it with #SystemStub in JUnit 5, or by using its SystemPropertiesRule subclass in JUnit4, or by executing the test code inside the SystemProperties execute method.

Related

Cucumber UndefinedStepException when running tests with external runner based on JUnit5 platform

I've written a very simple wrapper around the JUnit5 platform to run tests filtered by my external logics (which feeds path, packageIds, filterFilePath - handling of these was replaced by constant values in code samples to simplify the code snippets).
Here is how the main logic of this wrapper looks like:
public static void main(String[] args) throws JsonProcessingException {
String path = "D:\\repo\\cucumber-java-skeleton\\build\\libs";
String[] packageIds = "io.cucumber.skeleton".split(",");
String filterFilePath = "D:\\repo\\testrunner\\Tests\\Data\\DummyDataProject\\Java\\DummyJavaFilter.json";// contains JSON serialized list of correct test IDs
ClassLoader contextLoader = TestsLoader.GetLoader(path);
Thread.currentThread().setContextClassLoader(contextLoader);
final Launcher launcher = LauncherFactory.create();
List<TestResult> results = new ArrayList<TestResult>();
launcher.execute(getFilteredTestPlan(launcher, filterFilePath),
new TestRunnerExecutionListener(results));
ObjectMapper mapper = new ObjectMapper();
final String jsonResult = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(results);
System.out.print("-==TEST RESULTS START==-");
System.out.print(jsonResult);
System.out.print("-==TEST RESULTS END==-");
}
}
This method returns the TestPlan filtered to IDs which I want to execute (this logics works fine and as expected)
static TestPlan getFilteredTestPlan(Launcher launcher, String filterFilePath) {
String json = new String(Files.readAllBytes(Paths.get(filterFilePath)));
ObjectMapper mapper = new ObjectMapper();
List<String> testIds = mapper.readValue(json, new TypeReference<List<String>>() {});
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(testIds.stream().map(id -> selectUniqueId(id)).toArray(UniqueIdSelector[]::new))
.filters(includeClassNamePatterns(".*")).build();
return launcher.discover(request);
}
So the problem is, that when running this code on a perfectly fine and simple solution like this one provided by #M.P. Korstanje with some additions in gradle.build dependencies section:
testImplementation 'io.cucumber:cucumber-junit-platform-engine:' + cucumberVersion
For junit-platform integration, and these for building proper jar package with discoverable tests:
jar {
from configurations.testCompileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
from sourceSets.test.output
}
The solution itself runs perfectly smooth when step definitions are added and running tests as expected (using default gradle test task). But when running using code provided above - it fails to find step definitions and fails with io.cucumber.junit.platform.engine.UndefinedStepException even though the steps are present. Adding glue property to #CucumberOptions doesn't solve the issue as well (though shouldn't even be needed as step definitions are in the same package).
So I've spent lots of time digging through all available sources without any luck for a few days already, any help will be much appreciated.
I've removed all error handling and section that handles discovery of the tests as irrelevant to the problem, test IDs coming from this code as input are verified correctly.
Update:
When I am adding step definitions into a class contained in the same package which contains executor logics, it successfully discovers it, even though the glue setting points to a totally different package, it seems that these annotations are ignored, as code above discovers tests even when these annotations are removed completely:
#RunWith(Cucumber.class)
#CucumberOptions(plugin = { "pretty" }, glue = "io.cucumber.skeleton")
.
So after some experimentation, I came up with a dirty hack that does the trick:
System.setProperty("cucumber.glue", String.join(",", packageIds));//packageIds has to contain all Step Definitions pacakges
This adds system property that is later on handled by this code as in this documentation stated that it either gets properties file (which I've failed to add into Jar file as I'm pretty new to Java and Jar packaging specifics) or reads system properties in JVM which I have added. A more successful solution would be getting these properties from the target package that contains tests, which I've also failed to do without excessive searches and some pretty heavy reflection usage. The ideal solution is proper integration between the Cucumber Runner and Junit platform which is not yet in place or I've miss-used it somehow. Any better replies will be much appreciated and will be marked as a proper answer.

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

JUnitCore giving "No runnable methods" error in Eclipse plugin

I am trying to write an Eclipse plugin that can run JUnit tests and do something with the results. My plugin loads a given class correctly, but fails to run the JUnit tests and gives an error: initializationError(className): No runnable methods. When I run the test class using Result result = JUnitCore.runClasses(className.class); Failure failure : result.getFailures(); from within the same Eclipse instance, however, I don't get any errors.
I think my problem is the one that #gubby describes in the question java.lang.Exception: No runnable methods exception in running JUnits, but I don't know how to implement his suggestion to a solution which reads: "Solution is to load JUnitCore in the same ClassLoader as the tests themselves."
Here is a reduced version of my implementation (please assume that everything except the loading of the runnable methods work):
ClassLoader classLoader = ClassLoaderHelper.getClassLoader(FileFinder.getCurrentProject());
Class clazz = classLoader.loadClass(fileName.substring(0, fileName.indexOf(".class")));
Result result = JUnitCore.runClasses(clazz);
Failure failure : result.getFailures()
The code to get the ClassLoader is the following:
public static ClassLoader getClassLoader(IProject project) {
String[] classPathEntries = null;
try {
project.open(null);
IJavaProject javaProject = JavaCore.create(project);
classPathEntries = JavaRuntime.computeDefaultRuntimeClassPath(javaProject);
} catch (CoreException e1) {
e1.printStackTrace();
}
List<URL> urlList = new ArrayList<URL>();
for (String entry : classPathEntries) {
IPath path = new Path(entry);
URL url;
try {
url = path.toFile().toURI().toURL();
urlList.add(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
ClassLoader parentClassLoader = project.getClass().getClassLoader();
URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]);
URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);
return classLoader;
}
For Eclipse plugins, you have basically two options to have one plugin share a class loader with another.
Buddy Class Loading. Note that this breaks loose coupling, but it's easy to "implement" as you simply add two statements in the two respective plugins' MANIFEST.MF and an export statement as well. The following rules apply (from the link given above).
The bundle Y must specify the registered buddy policy (i.e. Eclipse-BuddyPolicy: registered)
The bundle X must specify the symbolic name of Y in the Eclipse-RegisterBuddy header (i.e Eclipse-RegisterBuddy: Y)
The bundle X must be dependent on a package exported by bundle Y. This can happen through either a Require-Bundle or Import-Package constraint.
Fragments: You can "attach" a fragment to a plugin. Both share the same class loader. Usually, this technique is used for things like plugin i18n, but it's also the recommended practice for adding unit tests to plugins. This way, the tests don't have to go into the same plugin, and possibly unneeded test classes or dependencies won't go in the production code.
There's a wizard for fragments in Eclipse, but they're basically plugins themselves, which declare a "host plugin".
So, you could consider putting your code into a fragment and attach that to the respective plugin containing the code-under-test. Or, if you need to reuse your code for different plugins and do not care about loose coupling, use Buddy Class Loading.
Also, check whether you have all the right dependencies in the plugin (e.g., org.junit). You need the JUnit contained in the Java Development Tools.

Spring jUnit Testing properties file

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/

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);
}
}

Categories