Reputation: 2321
Scenario: As a user, I want to login to the system
Given I am on my website
When I enter valid credentials
Then I am taken to the home page
The scenario name can be retrieved using the getName()
function. Is there a way to also get the step being executed (in Java
)? We foresee the use of this in logging and reporting.
So, for the scenario above, I am on my website
would be returned while the corresponding step definition is being executed.
Upvotes: 4
Views: 30370
Reputation: 1
As a newbie I could not comment the answer from AndyGee but if you want to have the actual name you have to use the .getName() method or .getUri + .getLine() to get something like an id (.getId() does not return a unique ID).
@BeforeStep
public void beforeStep(Scenario scenario){
System.out.println(scenario.getName().toString());
}
Currently we are using the .getUri() method and check the Uri against a substring to be a little more flexible in the future.
Upvotes: 0
Reputation: 31
Here's an update to handle the framework changes. The "testCase" field is hidden under the "delegate". I got this working with io.cucumber.java version 5.7.0
public String getStepText(io.cucumber.java.Scenario scenario){
String currentStepDescr = null;
//value currentStepDefIndex is tracked in the another class
int currentStepDefIndex = OtherClass.getStepIndex();
Field f = scenario.getClass().getDeclaredField("delegate");
f.setAccessible(true);
TestCaseState tcs = (TestCaseState) f.get(scenario);
Field f2 = tcs.getClass().getDeclaredField("testCase");
f2.setAccessible(true);
TestCase r = (TestCase) f2.get(tcs);
List<PickleStepTestStep> stepDefs = r.getTestSteps()
.stream()
.filter(x -> x instanceof PickleStepTestStep)
.map(x -> (PickleStepTestStep) x)
.collect(Collectors.toList());
PickleStepTestStep currentStepDef = stepDefs
.get(currentStepDefIndex);
currentStepDescr = currentStepDef.getStep().getText();
currentStepDefIndex += 1;
OtherClass.setStepIndex(currentStepDefIndex);
return currentStepDescr ;
}
Below are the dependencies in my pom.xml
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-core -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-core</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-jvm-deps -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm-deps</artifactId>
<version>1.0.6</version>
<scope>provided</scope>
</dependency>
Upvotes: 3
Reputation: 971
Grabbing the annotation using self-reflection seems more straightforward to me:
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@When("^User enters username and password$")
public void userEntersUsernameAndPassword() throws Throwable{
Method callingMethod = new Object() {} .getClass() .getEnclosingMethod();
Annotation myAnnotation = callingMethod.getAnnotations()[0];
System.out.println("myAnnotation=" + myAnnotation);
Results in:
[email protected](timeout=0, value=^User is in portal page$)
Upvotes: 1
Reputation: 894
I solved it using @BeforeStep & @AfterStep. It is a bit hacky, so use it only if you are know what you are doing.
public class StepDefBeginEndLogger {
private int currentStepDefIndex = 0;
@BeforeStep
public void doSomethingBeforeStep(Scenario scenario) throws Exception {
Field f = scenario.getClass().getDeclaredField("testCase");
f.setAccessible(true);
TestCase r = (TestCase) f.get(scenario);
//You need to filter out before/after hooks
List<PickleStepTestStep> stepDefs = r.getTestSteps()
.stream()
.filter(x -> x instanceof PickleStepTestStep)
.map(x -> (PickleStepTestStep) x)
.collect(Collectors.toList());
//This object now holds the information about the current step definition
//If you are using pico container
//just store it somewhere in your world state object
//and to make it available in your step definitions.
PickleStepTestStep currentStepDef = stepDefs
.get(currentStepDefIndex);
}
@AfterStep
public void doSomethingAfterStep(Scenario scenario) {
currentStepDefIndex += 1;
}
}
Upvotes: 3
Reputation: 49
Just leaving this here for future reference...
The current version of Cucumber (4.2.5) has the BeforeStep hook, but only provides access to the current running scenario.
What I did to extract the current step, was using reflection to access the steps within that scenario;
@BeforeStep
public void beforeStep(Scenario scn) throws Exception {
currentStepIndex++;
Field testCaseField = scn.getClass().getDeclaredField("testCase");
testCaseField.setAccessible(true);
TestCase tc = (TestCase) testCaseField.get(scn);
Field testSteps = tc.getClass().getDeclaredField("testSteps");
testSteps.setAccessible(true);
List<TestStep> teststeps = tc.getTestSteps();
try {
PickleStepTestStep pts = (PickleStepTestStep) teststeps.get(currentStepIndex);
getLog().info("########################");
getLog().info("##########STEP##########");
getLog().info(pts.getStepText());
getLog().info("########################");
currentStepIndex++;
} catch (Exception ignore) {
}
}
The only downside is, that you require a int currentStepIndex at class level, and need to add 1 with every @Before
or @BeforeStep
.
BE WARNED that the use of this type of reflection may fail to work in future releases of Cucumber, as the Cucumber team can decide to change their internals.
Upvotes: 2
Reputation: 586
I had this same question. I attempted to use rs79's answer but either I don't know what I'm actually doing with it or it doesn't work. Java gives me an "AmbiguousStepDefinitionException" or something like that. So I did it a different way. It takes a little work if you have a slew of step definitions but it works and is pretty simple:
@Then(value = "^The page should navigate to url \"([^\"])\"$", timeout = MAX_TIME)
public void the_page_should_navigate_to_url(String url) {
//below I use a custom class with a static method setStepName() which just sets a string field in the class
CustomClass.setStepName("Then The page should navigate to url " + url);
//Assert
}
Now you have access to the step name without needing any kind of complicated tool. Just use a get method to access the step variable in your custom class. Hope that helps.
Upvotes: 0
Reputation: 371
These hooks will help:
@BeforeStep
public void beforeStep(Scenario scenario){
System.out.println(scenario.toString());
}
@AfterStep
public void afterStep(Scenario scenario){
System.out.println(scenario.toString());
}
Upvotes: 0
Reputation: 504
I think the CucumberWithSerenity register a Listener which stores the current Step Name.
Try this in your Test-Runner:
//import net.serenitybdd.cucumber.CucumberWithSerenity;
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(...
And then in in your Step:
//import net.thucydides.core.model.TestStep;
//import net.thucydides.core.steps.StepEventBus;
if (!StepEventBus.getEventBus().isBaseStepListenerRegistered()) {
return "Unknown"; // CucumberWithSerenity is required.
}
String currentStepDescr = StepEventBus.getEventBus().getCurrentStep()
.transform(TestStep::getDescription)
.get();
Dependency:
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>${serenity.version}</version>
</dependency>
Upvotes: 3
Reputation: 2321
We solved this problem by wrapping the entire step as a parameter into the Step Definition. In other words, the step
Given I am on my website
translates into
'Given I am on my website'
And the step definition will actually accept a string parameter that will correspond to the step
@And("(.*)") //plus something specific to map step
public void Initialization(String step) throws Exception {
//do something with step
}
Upvotes: 2
Reputation: 382
you could add a step like
When I log in with the user 'user' and the password 'password'
and repeat this step whenever you need a login
You have to put the class containing the step definition in a package used by every Runner that will need the login.
Upvotes: -2
Reputation: 408
Being a newbie m not allowed to comment so here is some info, assuming you are using cucumber-jvm.
Short answer, No, Cucumber by itself doesnt have the option to read step names. You could use the method names to identify what was called.
Also, @BEFORE STEP / @AFTER STEP tags are not yet available so you will have to define the call for each step.
https://github.com/cucumber/cucumber-jvm/pull/838#issuecomment-234110573
or the testing framework like junit or testng could let you access the execution details - something like this: http://junit.org/junit4/javadoc/4.12/org/junit/rules/TestWatcher.html.
And if you really need the step names only for reporting purposes, you can simply parse the xml report that the testing framework generates.
Upvotes: 1
Reputation: 4323
Are you asking if it is possible to get some logging that indicates that the step When I enter valid credentials
is executed?
If so, the answer is yes.
Cucumber as such doesn't have a notion of logging so you would have to add your own favorite logging framework. Since Cucumber doesn't know about logging through your favorite log framework, you will have to add a log statement in each step you implement in Java.
I have never seen the need for logging myself. The execution log from Maven, or whatever build tool you are using, have been sufficient for me for a long time.
The reports include the steps executed so that case is covered.
Upvotes: 0