JUnit4DescriptionGenerator.java
package org.jbehave.core.junit;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import org.jbehave.core.annotations.AfterScenario.Outcome;
import org.jbehave.core.annotations.ScenarioType;
import org.jbehave.core.annotations.Scope;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.Keywords;
import org.jbehave.core.embedder.AllStepCandidates;
import org.jbehave.core.embedder.PerformableTree;
import org.jbehave.core.embedder.PerformableTree.ExamplePerformableScenario;
import org.jbehave.core.embedder.PerformableTree.PerformableScenario;
import org.jbehave.core.embedder.PerformableTree.PerformableStory;
import org.jbehave.core.model.GivenStories;
import org.jbehave.core.model.Lifecycle;
import org.jbehave.core.model.Scenario;
import org.jbehave.core.model.Story;
import org.jbehave.core.steps.BeforeOrAfterStep;
import org.jbehave.core.steps.ConditionalStepCandidate;
import org.jbehave.core.steps.StepCandidate;
import org.jbehave.core.steps.StepType;
import org.junit.runner.Description;
public class JUnit4DescriptionGenerator {
private final TextManipulator textManipulator = new TextManipulator();
private final Configuration configuration;
private final AllStepCandidates allStepCandidates;
private String previousNonAndStep;
private int testCases;
private final JUnit4Test junitTestMeta;
public JUnit4DescriptionGenerator(AllStepCandidates allStepCandidates, Configuration configuration) {
this.configuration = configuration;
this.allStepCandidates = allStepCandidates;
this.junitTestMeta = new JUnit4Test() {
@Override
public Class<? extends Annotation> annotationType() {
return JUnit4Test.class;
}
};
}
public List<Description> createDescriptionsFrom(PerformableTree performableTree) {
List<Description> storyDescriptions = new ArrayList<>();
for (PerformableStory performableStory : performableTree.getRoot().getStories()) {
if (!performableStory.isExcluded()) {
Story story = performableStory.getStory();
Lifecycle lifecycle = story.getLifecycle();
Description storyDescription = createDescriptionForStory(story);
addBeforeOrAfterStep(allStepCandidates.getBeforeStorySteps(false), storyDescription,
"@BeforeStory: ");
addSteps(storyDescription, lifecycle.getBeforeSteps(Scope.STORY));
if (story.hasGivenStories()) {
insertGivenStories(story.getGivenStories(), storyDescription);
}
List<PerformableScenario> scenarios = performableStory.getScenarios();
for (Description scenarioDescription : getScenarioDescriptions(lifecycle, scenarios)) {
storyDescription.addChild(scenarioDescription);
}
addSteps(storyDescription, lifecycle.getAfterSteps(Scope.STORY, Outcome.ANY));
addBeforeOrAfterStep(allStepCandidates.getAfterStorySteps(false), storyDescription,
"@AfterStory: ");
storyDescriptions.add(storyDescription);
}
}
return storyDescriptions;
}
public Description createDescriptionsFrom(Lifecycle lifecycle, PerformableScenario performableScenario) {
Scenario scenario = performableScenario.getScenario();
Description scenarioDescription = createDescriptionForScenario(scenario);
if (performableScenario.hasExamples() && !scenario.getGivenStories().requireParameters()) {
insertDescriptionForExamples(lifecycle, performableScenario, scenarioDescription);
} else {
addScenarioSteps(lifecycle, ScenarioType.NORMAL, scenario, scenarioDescription);
}
return scenarioDescription;
}
private void addScenarioSteps(Lifecycle lifecycle, ScenarioType scenarioType, Scenario scenario,
Description scenarioDescription) {
addBeforeOrAfterScenarioStep(allStepCandidates::getBeforeScenarioSteps, scenarioType, scenarioDescription,
"@BeforeScenario: ");
addSteps(scenarioDescription, lifecycle.getBeforeSteps(Scope.SCENARIO));
if (scenario.hasGivenStories()) {
insertGivenStories(scenario.getGivenStories(), scenarioDescription);
}
addScenarioSteps(lifecycle, scenarioDescription, scenario);
addSteps(scenarioDescription, lifecycle.getAfterSteps(Scope.SCENARIO, Outcome.ANY));
addBeforeOrAfterScenarioStep(allStepCandidates::getAfterScenarioSteps, scenarioType, scenarioDescription,
"@AfterScenario: ");
}
private void addScenarioSteps(Lifecycle lifecycle, Description scenarioDescription, Scenario scenario) {
List<String> beforeSteps = lifecycle.getBeforeSteps(Scope.STEP);
List<String> afterSteps = lifecycle.getAfterSteps(Scope.STEP);
previousNonAndStep = null;
String tempPreviousNonAndStep = null;
for (String scenarioStep : scenario.getSteps()) {
addSteps(scenarioDescription, beforeSteps);
previousNonAndStep = tempPreviousNonAndStep;
addStep(scenarioDescription, scenarioStep);
tempPreviousNonAndStep = previousNonAndStep;
addSteps(scenarioDescription, afterSteps);
}
}
private void addBeforeOrAfterScenarioStep(Function<ScenarioType, List<BeforeOrAfterStep>> stepsProvider,
ScenarioType scenarioType, Description description, String stepName) {
List<BeforeOrAfterStep> beforeOrAfterSteps = new ArrayList<>();
beforeOrAfterSteps.addAll(stepsProvider.apply(scenarioType));
beforeOrAfterSteps.addAll(stepsProvider.apply(ScenarioType.ANY));
addBeforeOrAfterStep(beforeOrAfterSteps, description, stepName);
}
private void addBeforeOrAfterStep(List<BeforeOrAfterStep> beforeOrAfterSteps, Description description,
String stepPrefix) {
beforeOrAfterSteps.forEach(steps -> {
testCases++;
Method method = steps.getMethod();
String stepName = uniquify(stepPrefix + steps.getMethod().getName());
Description testDescription = Description.createTestDescription(method.getDeclaringClass(), stepName,
junitTestMeta);
description.addChild(testDescription);
});
}
public String uniquify(String string) {
return textManipulator.uniquify(string);
}
public int getTestCases() {
return testCases;
}
private void insertGivenStories(GivenStories givenStories, Description parentDescription) {
for (String path : givenStories.getPaths()) {
addGivenStory(parentDescription, path);
}
}
private void addGivenStory(Description parentDescription, String path) {
parentDescription.addChild(Description.createSuiteDescription(uniquify(getFilename(path)), junitTestMeta));
testCases++;
}
private String getFilename(String path) {
return path.substring(path.lastIndexOf('/') + 1).split("#")[0];
}
private void insertDescriptionForExamples(Lifecycle lifecycle, PerformableScenario performableScenario,
Description scenarioDescription) {
Scenario scenario = performableScenario.getScenario();
for (ExamplePerformableScenario examplePerformableScenario : performableScenario.getExamples()) {
Description exampleRowDescription = Description.createSuiteDescription(
configuration.keywords().examplesTableRow() + " " + examplePerformableScenario.getParameters());
scenarioDescription.addChild(exampleRowDescription);
addScenarioSteps(lifecycle, ScenarioType.EXAMPLE, scenario, exampleRowDescription);
}
}
private void addSteps(Description description, List<String> steps) {
previousNonAndStep = null;
steps.forEach(step -> addStep(description, step));
}
private void addStep(Description description, String step) {
String stringStepOneLine = stripLinebreaks(step);
StepCandidate matchingStep = findMatchingStep(step);
if (matchingStep == null) {
addNonExistingStep(description, stringStepOneLine, step);
} else {
addExistingStep(description, stringStepOneLine, matchingStep);
}
}
private void addExistingStep(Description description, String stringStepOneLine, StepCandidate matchingStep) {
if (matchingStep.isComposite()) {
addCompositeSteps(description, stringStepOneLine, matchingStep);
} else if (matchingStep instanceof ConditionalStepCandidate) {
addConditionalStep(description, stringStepOneLine);
} else {
addRegularStep(description, stringStepOneLine, matchingStep);
}
}
private void addNonExistingStep(Description description, String stringStepOneLine, String stepAsString) {
Keywords keywords = configuration.keywords();
if (keywords.isIgnorableStep(stepAsString)) {
if (isStep(keywords.stepWithoutStartingWord(stepAsString, StepType.IGNORABLE))) {
addIgnorableStep(description, stringStepOneLine);
}
} else {
addPendingStep(description, stringStepOneLine);
}
}
private boolean isStep(String stepAsString) {
Keywords keywords = configuration.keywords();
for (String stepStartingWord : keywords.startingWordsByType().values()) {
if (keywords.stepStartsWithWord(stepAsString, stepStartingWord)) {
return true;
}
}
return false;
}
private void addIgnorableStep(Description description, String stringStep) {
testCases++;
description.addChild(Description.createSuiteDescription(stringStep));
}
private void addPendingStep(Description description, String stringStep) {
testCases++;
description.addChild(Description.createSuiteDescription(uniquify("[PENDING] " + stringStep)));
}
private void addConditionalStep(Description description, String stringStep) {
testCases++;
description.addChild(Description.createSuiteDescription(uniquify(stringStep)));
}
private void addRegularStep(Description description, String stringStep, StepCandidate step) {
testCases++;
// JUnit and the Eclipse JUnit view needs to be touched/fixed in order to make the JUnit view jump to the
// corresponding test method accordingly. For now we have to live, that we end up in the correct class.
description.addChild(Description.createTestDescription(step.getStepsType(), uniquify(stringStep),
junitTestMeta));
}
private void addCompositeSteps(Description description, String stringStep, StepCandidate step) {
Description testDescription = Description.createSuiteDescription(uniquify(stringStep), junitTestMeta);
addSteps(testDescription, Arrays.asList(step.composedSteps()));
description.addChild(testDescription);
}
private List<Description> getScenarioDescriptions(Lifecycle lifecycle,
List<PerformableScenario> performableScenarios) {
List<Description> scenarioDescriptions = new ArrayList<>();
for (PerformableScenario scenario : performableScenarios) {
if (!scenario.isExcluded()) {
scenarioDescriptions.add(createDescriptionsFrom(lifecycle, scenario));
}
}
return scenarioDescriptions;
}
private StepCandidate findMatchingStep(String stringStep) {
for (StepCandidate step : allStepCandidates.getRegularSteps()) {
if (step.matches(stringStep, previousNonAndStep)) {
if (step.getStepType() != StepType.AND) {
previousNonAndStep = step.getStartingWord() + " ";
}
return step;
}
}
return null;
}
private String stripLinebreaks(String stringStep) {
if (stringStep.indexOf('\n') != -1) {
return stringStep.substring(0, stringStep.indexOf('\n'));
}
return stringStep;
}
private Description createDescriptionForStory(Story story) {
return Description.createSuiteDescription(uniquify(story.getName()));
}
private Description createDescriptionForScenario(Scenario scenario) {
return Description.createSuiteDescription(
configuration.keywords().scenario() + " " + uniquify(scenario.getTitle()));
}
public @interface JUnit4Test {
}
}