Cucumber BDD different features requiring similar step - java

In my application I have two feature files:
processing.feature
copy.feature
For these I have two step definition files:
Processing Feature:
public class ProcessingFeature {
#Qualifier("moverA")
#Autowired
private IFileMover mover;
#Given("check file is available")
public void load() {
}
#When("the file is there at the location")
public void moveFile() {
output= mover.move(info);
}
}
Copy Feature:
public class CopyFeature {
#Qualifier("moverB")
#Autowired
private IFileMover mover;
#Given("check file is available")
public void load() {
}
#When("the file is there at the location")
public void moveFile() {
output= mover.move(info);
}
}
In both my feature files, I run check file is available as #Given step. My first question is, they are identical step definitions sitting in both step def file. What is the correct pattern so this code is not duplicated and both features can make use of it?
Secondly, both features run the file is there at the location but one uses MoverA & the Other uses MoverB. But the idea remains same. Again I don't like the duplication so how best can I re-use maybe through some abstraction ?
Ofcourse I get cucumber.runtime.DuplicateStepDefinitionException: Duplicate step definitions exception but I am curious to see what is correct pattern to solve these kind of problems

Steps can only be defined once. Step definitions are global, and not specific to a scenario or feature. Since a step can only have one definition, code duplication is not an issue.

You need to read the following page from Cucumber : https://cucumber.io/docs/gherkin/step-organization/
It will help you organize correctly your glue code.
As Greg said, the step definitions are global, it is an anti-pattern to create feature-specific steps :
https://cucumber.io/docs/guides/anti-patterns/#feature-coupled-step-definitions
In your feature file, I think there is a confusion between When steps (action : ie copying the file or processing the file) and Then steps (expected result : the file should be at which location).
Your step definitions should be more like :
#When("I copy the following file:(.*)")
public void copyFile(String fileName) {
xxxx calling system under test xxxx
}
#When("I process the following file:(.*)")
public void processFile(String fileName) {
xxxx calling system under test xxxx
}
#Then("the file is at the following location:(.*)")
public void checkFileExistence(String fileLocation) {
Assert.assertTrue(xxxxx);
}
To share state between steps, you can use the World object pattern :
cf https://cucumber.io/docs/cucumber/state/

This is downside when using plain text file and step mapping for BDD implementation. Alternate to this and similar issues because of additional feature file layer is, using pure java implementation of scenario. It will eliminate extra layer of feature files and certainly step definition files and allows you to follow behavior driven development.

Related

Creating an 'Empty Shell' Jar

I'm looking on guidance on how I can essentially create an 'empty shell' jar with maven. The idea is I have a java project, and I want to export the my.project.api classes (with package) into its own jar without saving the methods / constructors actual code inside.
For example, lets say I have the following:
public class Test {
public void doSomething(String message) {
System.out.println(message);
}
}
I want to export a separate jar which would keep its package declaration, and export as:
public class Test {
public void doSomething(String message) {}
}
The reasoning for this is the project itself is exclusive, but I want to allow other developers to make their own integrations without the need of the physical product / project. This way by them hooking into say my.project.api.Test, they'd be able to see the methods and do as they wish.
Hopefully this clarifies enough, it would export as a separate jar maybe as 'MyProject-API.jar' or something.
Thanks!
This very much looks like a use case for interfaces.

How to run a method after passing through a tag in the feature file?

I have a scenario in my feature file that contains two tags
#tag1, #tag2
Scenario: This is a test
Given I open the website
When I log in
Then the account page is open
Now #tag is part of a cucumber serenity runner and this works in isolation (before the implementation of #tag2):
#RunWith(CucumberWithSerenity.class)
#CucumberOptions(features="...features/", glue = {".../steps"}, tags = {"#tag1"} )
public class Tag1Runner extends Hooks {
#BeforeClass
public static void startAppium() {
AppiumServerController.startAppiumServer();
}
#AfterClass
public static void stopAppium() {
AppiumServerController.stopAppiumServer();
}
Now what I also want to do is run #tag2, but this only runs after the test has been completed. This is because the method that occurs in #tag2 should not be part of the scenario to test, but should complete a clean up after the test has ran.
Now if I include the following method below, when I run the feature file as it is displayed above, nothing happens.
How can I implement #tag2 to work as mentioned?
public class Hooks{
#After("#tag2")
public void completeCleanUp() {
//code to perform clean up...
}
}
If you are trying to run an After hook after each scenario tagged with "#tag1" in your example, you should mark the After hook with "#tag1". For more information on tagged hooks, please refer to the Cucumber documentation.

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

jUnit4.0 : how to know current test method name?

I've implemented a feature in my jUnit tests that takes, for every test case, a fresh copy of a data source. This copy is taken in a folder specific for each test case. The idea is that every test case can start from a clean situation, manipulate it and let it as such after the run. This is often useful when the test fails for analysing the problem.
For now I have to call this feature directly in the test method because I don't know how to retrieve the current test name:
public void testTest1() {
TestHelper th=TestHelper.create("testTest1",subPathToDataSource);
// do the test...
Path dataPath = th.getDataPath();
...
}
I would like to be able to write something like this:
Path dataPath;
#Before
public initTest() {
th=TestHelper.create(SomeJUnitObject.getCurrentTestName(),subPathToDataSource);
...
}
public void testTest1() {
// do the test...
Path dataPath = th.getDataPath();
...
}
Until now I found as answers : "You don't need to know that"... But I do need it !
Is this possible ?
Kind regards
Look at the TestName rule.
You should be able to add in your test class:
#Rule TestName name=new TestName();
And then access it.
(On phone, so can't check versions support/details - might be 4.x only)
Here is an alternative approach; create an abstract class which your "real" test classes inherit.
I have several such examples in my projects and here I will give one, mainly testing for individual JSON Patch operations.
All my test files are JSON, and located under an appropriately named resource directory. The base, abstract class is JsonPatchOperationTest. And here is the full code of AddOperationTest which tests for JSON Patch's add operation:
public final class AddOperationTest
extends JsonPatchOperationTest
{
public AddOperationTest()
throws IOException
{
super("add");
}
}
And that's it! Not even one test method in this class, but of course your implementation may vary.
In your case you probably want to pass the directory name as a constructor argument, or the like.

Categories