JBehave 如何在步骤之间保持对象状态
JBehave how to persist objects state between steps
我正在使用 JBehave 编写 BDD 集成测试。
问题:JBehave 在执行单个步骤时清除对象(实例变量)的状态
代码:
步骤定义:
public class StepDefs {
private String str;
@Given("step represents a precondition to an $event")
public void given(String event){
str=event;
System.out.println("Given: "+str);
}
@When("step represents the occurrence of the event")
public void when() {
System.out.println("When: "+str);
}
@Then("step represents the outcome of the event")
public void then() {
}
}
故事:
Sample story
Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development
Scenario: A scenario is a collection of executable steps of different type
Given step represents a precondition to an event
When step represents the occurrence of the event
Then step represents the outcome of the event
JBehaveJUnitTestRunner:
@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {
private CrossReference xref = new CrossReference();
public JBehaveTestsRunner() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
.doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
}
@Override
public Configuration configuration() {
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
.useStoryReporterBuilder(
new StoryReporterBuilder().withFormats(Format.HTML, Format.CONSOLE, Format.STATS)
.withViewResources(viewResources).withFailureTrace(true).withFailureTraceCompression(false)
.withCrossReference(xref));
}
@Override
public InjectableStepsFactory stepsFactory() {
return new ScanningStepsFactory(configuration(), "stepdefs");
}
@Override
public List<String> storyPaths() {
StoryFinder finder = new StoryFinder();
return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Simple.story"), null);
}
}
实际输出:
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=true,ignoreFailureInView=true,verboseFailures=true,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]
(BeforeStories)
Running story stories/Simple.story
Sample story
(stories/Simple.story)
Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development
Scenario: A scenario is a collection of executable steps of different type
**Given: event**
Given step represents a precondition to an event
**When: null**
When step represents the occurrence of the event
Then step represents the outcome of the event
(AfterStories)
Generating reports view to 'C:\WORKING\lunaworkspace\pkeautomation\target\jbehave' using formats '[html, console, stats, junitscenarioreporter]' and view properties '{decorateNonHtml=true}'
log4j:WARN No appenders could be found for logger (freemarker.cache).
log4j:WARN Please initialize the log4j system properly.
Reports view generated with 3 stories (of which 1 pending) containing 2 scenarios (of which 1 pending)
从输出中可以看出:在给定步骤中,我接受了一个字符串参数,我将其初始化为实例变量 "str",同时将值打印到控制台,我可以成功看到它。但是当第二步即执行步骤时,我得到 null 作为实例变量 "str" 的值。如何让 JBehave 在执行单个步骤后不清除对象状态?
我已经有一段时间没有使用 JBehave 了。您可能正在为每个 stepsFactory()
调用创建一个新的 ScanningStepsFactory
。尝试在构造函数中只创建其中一个,然后将该实例传回,这样您就不会为每次调用都创建一个新实例。
如果失败,请尝试使用示例中的 InstanceStepsFactory
here:
public abstract class NoughtsAndCrossesStory extends JUnitStory {
public NoughtsAndCrossesStory() {
Configuration configuration = new MostUsefulConfiguration()
.useStoryPathResolver(new UnderscoredCamelCaseResolver(""))
.useStoryReporterBuilder(new StoryReporterBuilder()
.withCodeLocation(CodeLocations.codeLocationFromClass(this.getClass()))
.withDefaultFormats()
.withFormats(CONSOLE, TXT)
.withFailureTrace(true));
useConfiguration(configuration);
WindowControl windowControl = new WindowControl();
addSteps(new InstanceStepsFactory(configuration,new GridSteps(windowControl), new BeforeAndAfterSteps(windowControl)).createCandidateSteps());
}
}
您需要创建某种持久存储库对象来保存您的字符串(在上面的示例中,windowControl
持续存在)。
public class BeforeAndAfterSteps extends Steps {
private final WindowControl windowControl;
public BeforeAndAfterSteps(WindowControl windowControl) {
this.windowControl = windowControl;
}
@BeforeScenario
public void beforeScenarios() throws Exception {
windowControl.reset();
}
@AfterScenario
public void afterScenarios() throws Exception {
windowControl.destroy();
}
}
这不仅可以让您跨步骤保持状态,还可以在场景之间保持状态。请注意,这通常被认为是一种不好的做法;我在这里使用它来确保状态 而不是 在场景之间持续存在,但理论上你可以用它来,例如,在 运行 你的测试套件之前初始化默认数据.
对于任何前来寻找此问题答案的人,我确实通过 JBehave google 社区社区的帮助找到了正确的解决方案。解决方案非常简单,而不是使用 ScanningStepFactory
使用 InstanceStepsFactory
并且应该保留对象的状态。 Link 参与讨论:Google Group discussion
任何来这里回答的人的片段:
package runner;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.CrossReference;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
//import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
//import org.jbehave.core.steps.ScanningStepsFactory;
import org.junit.runner.RunWith;
import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;
import stepdefs.StepDefs;
//import stepdefs.BarStep;
//import stepdefs.FooStep;
@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {
private CrossReference xref = new CrossReference();
public JBehaveTestsRunner() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
.doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
}
@Override
public Configuration configuration() {
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
.useStoryReporterBuilder(new StoryReporterBuilder()
.withFormats(Format.HTML, Format.CONSOLE, Format.STATS).withViewResources(viewResources)
.withFailureTrace(true).withFailureTraceCompression(false).withCrossReference(xref));
}
/*@Override
public List<CandidateSteps> candidateSteps() {
return new InstanceStepsFactory(configuration(), new FooStep(), new BarStep(), new StepDefs())
.createCandidateSteps();
}*/
@Override
public InjectableStepsFactory stepsFactory() {
return new InstanceStepsFactory(configuration(), new StepDefs());
// return new ScanningStepsFactory(configuration(), "stepdefinitions");
}
@Override
public List<String> storyPaths() {
StoryFinder finder = new StoryFinder();
return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Sample.story"),
null);
}
}
我正在使用 JBehave 编写 BDD 集成测试。
问题:JBehave 在执行单个步骤时清除对象(实例变量)的状态
代码:
步骤定义:
public class StepDefs {
private String str;
@Given("step represents a precondition to an $event")
public void given(String event){
str=event;
System.out.println("Given: "+str);
}
@When("step represents the occurrence of the event")
public void when() {
System.out.println("When: "+str);
}
@Then("step represents the outcome of the event")
public void then() {
}
}
故事:
Sample story
Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development
Scenario: A scenario is a collection of executable steps of different type
Given step represents a precondition to an event
When step represents the occurrence of the event
Then step represents the outcome of the event
JBehaveJUnitTestRunner:
@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {
private CrossReference xref = new CrossReference();
public JBehaveTestsRunner() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
.doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
}
@Override
public Configuration configuration() {
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
.useStoryReporterBuilder(
new StoryReporterBuilder().withFormats(Format.HTML, Format.CONSOLE, Format.STATS)
.withViewResources(viewResources).withFailureTrace(true).withFailureTraceCompression(false)
.withCrossReference(xref));
}
@Override
public InjectableStepsFactory stepsFactory() {
return new ScanningStepsFactory(configuration(), "stepdefs");
}
@Override
public List<String> storyPaths() {
StoryFinder finder = new StoryFinder();
return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Simple.story"), null);
}
}
实际输出:
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=true,ignoreFailureInView=true,verboseFailures=true,verboseFiltering=false,storyTimeouts=300,threads=1,failOnStoryTimeout=false]
(BeforeStories)
Running story stories/Simple.story
Sample story
(stories/Simple.story)
Narrative:
In order to communicate effectively to the business some functionality
As a development team
I want to use Behaviour-Driven Development
Scenario: A scenario is a collection of executable steps of different type
**Given: event**
Given step represents a precondition to an event
**When: null**
When step represents the occurrence of the event
Then step represents the outcome of the event
(AfterStories)
Generating reports view to 'C:\WORKING\lunaworkspace\pkeautomation\target\jbehave' using formats '[html, console, stats, junitscenarioreporter]' and view properties '{decorateNonHtml=true}'
log4j:WARN No appenders could be found for logger (freemarker.cache).
log4j:WARN Please initialize the log4j system properly.
Reports view generated with 3 stories (of which 1 pending) containing 2 scenarios (of which 1 pending)
从输出中可以看出:在给定步骤中,我接受了一个字符串参数,我将其初始化为实例变量 "str",同时将值打印到控制台,我可以成功看到它。但是当第二步即执行步骤时,我得到 null 作为实例变量 "str" 的值。如何让 JBehave 在执行单个步骤后不清除对象状态?
我已经有一段时间没有使用 JBehave 了。您可能正在为每个 stepsFactory()
调用创建一个新的 ScanningStepsFactory
。尝试在构造函数中只创建其中一个,然后将该实例传回,这样您就不会为每次调用都创建一个新实例。
如果失败,请尝试使用示例中的 InstanceStepsFactory
here:
public abstract class NoughtsAndCrossesStory extends JUnitStory {
public NoughtsAndCrossesStory() {
Configuration configuration = new MostUsefulConfiguration()
.useStoryPathResolver(new UnderscoredCamelCaseResolver(""))
.useStoryReporterBuilder(new StoryReporterBuilder()
.withCodeLocation(CodeLocations.codeLocationFromClass(this.getClass()))
.withDefaultFormats()
.withFormats(CONSOLE, TXT)
.withFailureTrace(true));
useConfiguration(configuration);
WindowControl windowControl = new WindowControl();
addSteps(new InstanceStepsFactory(configuration,new GridSteps(windowControl), new BeforeAndAfterSteps(windowControl)).createCandidateSteps());
}
}
您需要创建某种持久存储库对象来保存您的字符串(在上面的示例中,windowControl
持续存在)。
public class BeforeAndAfterSteps extends Steps {
private final WindowControl windowControl;
public BeforeAndAfterSteps(WindowControl windowControl) {
this.windowControl = windowControl;
}
@BeforeScenario
public void beforeScenarios() throws Exception {
windowControl.reset();
}
@AfterScenario
public void afterScenarios() throws Exception {
windowControl.destroy();
}
}
这不仅可以让您跨步骤保持状态,还可以在场景之间保持状态。请注意,这通常被认为是一种不好的做法;我在这里使用它来确保状态 而不是 在场景之间持续存在,但理论上你可以用它来,例如,在 运行 你的测试套件之前初始化默认数据.
对于任何前来寻找此问题答案的人,我确实通过 JBehave google 社区社区的帮助找到了正确的解决方案。解决方案非常简单,而不是使用 ScanningStepFactory
使用 InstanceStepsFactory
并且应该保留对象的状态。 Link 参与讨论:Google Group discussion
任何来这里回答的人的片段:
package runner;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.CrossReference;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
//import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
//import org.jbehave.core.steps.ScanningStepsFactory;
import org.junit.runner.RunWith;
import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;
import stepdefs.StepDefs;
//import stepdefs.BarStep;
//import stepdefs.FooStep;
@RunWith(JUnitReportingRunner.class)
public class JBehaveTestsRunner extends JUnitStories {
private CrossReference xref = new CrossReference();
public JBehaveTestsRunner() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
.doIgnoreFailureInView(true).doVerboseFailures(true);// .useThreads(1);
}
@Override
public Configuration configuration() {
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
return new MostUsefulConfiguration().useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()))
.useStoryReporterBuilder(new StoryReporterBuilder()
.withFormats(Format.HTML, Format.CONSOLE, Format.STATS).withViewResources(viewResources)
.withFailureTrace(true).withFailureTraceCompression(false).withCrossReference(xref));
}
/*@Override
public List<CandidateSteps> candidateSteps() {
return new InstanceStepsFactory(configuration(), new FooStep(), new BarStep(), new StepDefs())
.createCandidateSteps();
}*/
@Override
public InjectableStepsFactory stepsFactory() {
return new InstanceStepsFactory(configuration(), new StepDefs());
// return new ScanningStepsFactory(configuration(), "stepdefinitions");
}
@Override
public List<String> storyPaths() {
StoryFinder finder = new StoryFinder();
return finder.findPaths(CodeLocations.codeLocationFromClass(getClass()), Arrays.asList("**/Sample.story"),
null);
}
}