Created
December 1, 2013 19:30
-
-
Save eleco/7739765 to your computer and use it in GitHub Desktop.
jbehave spring example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
public class Trade { | |
int price; | |
Trade (int price){ | |
this.price = price; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import org.springframework.stereotype.Component; | |
@Component | |
public class TradeService { | |
public boolean validate(Trade trade) { | |
return (trade.price>0); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import org.jbehave.core.configuration.Keywords; | |
import org.jbehave.core.reporters.*; | |
import java.io.File; | |
public class CustomHtmlOutput extends HtmlTemplateOutput { | |
public CustomHtmlOutput (File file, Keywords keywords){ | |
super(file, keywords, new FreemarkerProcessor(CustomHtmlOutput.class),"custom-html-output.ftl"); | |
} | |
public static final Format FORMAT = new Format("HTML"){ | |
@Override | |
public StoryReporter createStoryReporter(FilePrintStreamFactory factory, StoryReporterBuilder storyReporterBuilder){ | |
factory.useConfiguration(storyReporterBuilder.fileConfiguration("html")); | |
return new CustomHtmlOutput(factory.getOutputFile(),storyReporterBuilder.keywords()) ; | |
} | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import org.jbehave.core.InjectableEmbedder; | |
import org.jbehave.core.configuration.Configuration; | |
import org.jbehave.core.io.StoryFinder; | |
import org.jbehave.core.steps.spring.SpringStepsFactory; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.context.ApplicationContext; | |
import org.springframework.test.context.ContextConfiguration; | |
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | |
import static org.jbehave.core.io.CodeLocations.codeLocationFromClass; | |
@RunWith(SpringJUnit4ClassRunner.class) | |
@ContextConfiguration(locations={"classpath:spring-config.xml"}) | |
public class JBehaveRunner extends InjectableEmbedder { | |
protected java.util.List<String> storyPaths (){ | |
return new StoryFinder().findPaths(codeLocationFromClass(this.getClass()), "*.story", "") ; | |
} | |
@Autowired | |
public ApplicationContext applicationContext; | |
@Test | |
public void run() throws Throwable{ | |
Configuration configuration = injectedEmbedder().configuration(); | |
configuration.useStoryReporterBuilder(configuration.storyReporterBuilder().withFormats(CustomHtmlOutput.FORMAT)); | |
injectedEmbedder().useCandidateSteps(new SpringStepsFactory(injectedEmbedder().configuration(),applicationContext).createCandidateSteps()); | |
injectedEmbedder().runStoriesAsPaths(storyPaths()) ; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import javax.annotation.PostConstruct; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class ScenarioContext { | |
private List<Trade> trades=new ArrayList(); | |
public void addTrade(Trade trade){ | |
trades.add(trade); | |
} | |
public ScenarioContext(){ | |
trades = new ArrayList(); | |
} | |
@PostConstruct | |
public void resetTrades(){ | |
trades.clear(); | |
} | |
public Trade getFirstTrade(){ | |
return trades.get(0); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import org.jbehave.core.annotations.BeforeScenario; | |
import org.springframework.beans.factory.ObjectFactory; | |
import org.springframework.beans.factory.config.Scope; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
public class ScenarioScope implements Scope { | |
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap(); | |
@BeforeScenario | |
public void startScenario(){ | |
cache.clear(); | |
} | |
@Override | |
public Object get(String name, ObjectFactory objectFactory) throws IllegalStateException{ | |
if (!cache.containsKey(name)){ | |
cache.putIfAbsent(name, objectFactory.getObject()); | |
} | |
return cache.get(name); | |
} | |
@Override | |
public Object resolveContextualObject(String name){ | |
return null; | |
} | |
@Override | |
public Object remove(String name){ | |
return cache.remove(name); | |
} | |
@Override | |
public void registerDestructionCallback(String name, Runnable callback){} | |
@Override | |
public String getConversationId(){ | |
return "scenario scope"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package foo.bar; | |
import org.jbehave.core.annotations.Given; | |
import org.jbehave.core.annotations.Then; | |
import org.jbehave.core.annotations.When; | |
import org.jbehave.core.model.ExamplesTable; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Component; | |
import javax.annotation.Resource; | |
import static junit.framework.Assert.assertTrue; | |
import static org.junit.Assert.assertFalse; | |
@Component | |
public class TradeStep { | |
@Resource | |
private TradeService tradeService; | |
@Autowired | |
ScenarioContext scenarioContext; | |
//private List<Trade> trades; | |
private boolean validationResult; | |
@Given ("a table: $table") | |
public void atable(ExamplesTable table){ | |
} | |
@Given("a trade with a positive price") | |
public void aTradeWithAPositivePrice() { | |
scenarioContext.addTrade( new Trade(1)); | |
} | |
@Given("a trade with a negative price") | |
public void aTradeWithANegativePrice() { | |
scenarioContext.addTrade ( new Trade(-1)); | |
} | |
@When("the trade is validated") | |
public void theTradeIsValidated() { | |
validationResult = tradeService.validate(scenarioContext.getFirstTrade()); | |
} | |
@Then("trade fails validation") | |
public void tradeFailsValidation() { | |
assertFalse(validationResult); | |
} | |
@Then("trade passes validation") | |
public void tradePassesValidation() { | |
assertTrue(validationResult); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#ftl strip_whitespace=true> | |
<#macro renderMultiline text>${text?html?replace("\n", "<br/>")}</#macro> | |
<#macro renderMeta meta> | |
<div class="meta"> | |
<div class="keyword">${keywords.meta}</div> | |
<#assign metaProperties=meta.getProperties()> | |
<#list metaProperties.keySet() as name> | |
<#assign property = metaProperties.get(name)> | |
<div class="property">${keywords.metaProperty}${name?html} ${property?html}</div> | |
</#list> | |
</div> | |
</#macro> | |
<#macro renderNarrative narrative> | |
<div class="narrative"><h2>${keywords.narrative}</h2> | |
<div class="element inOrderTo"><span class="keyword inOrderTo">${keywords.inOrderTo}</span> ${narrative.inOrderTo}</div> | |
<div class="element asA"><span class="keyword asA">${keywords.asA}</span> ${narrative.asA}</div> | |
<div class="element iWantTo"><span class="keyword iWantTo">${keywords.iWantTo}</span> ${narrative.iWantTo}</div> | |
</div> | |
</#macro> | |
<#macro renderGivenStories givenStories> | |
<div class="givenStories"> | |
<div class="keyword">${keywords.givenStories}</div> | |
<#list givenStories.getStories() as givenStory> | |
<div class="givenStory">${givenStory.path}</div> | |
</#list> | |
</div> | |
</#macro> | |
<#macro renderTable table> | |
<#assign rows=table.getRows()> | |
<#assign headers=table.getHeaders()> | |
<table> | |
<tbody> | |
<#list headers as header> | |
<tr> | |
<#assign cell=rows[0].get(header)> | |
<td>${header?html}</td> | |
<td>${cell?html}</td> | |
</tr> | |
</#list> | |
</tbody> | |
</table> | |
</#macro> | |
<#macro renderOutcomes table> | |
<#assign outcomes=table.getOutcomes()> | |
<#assign fields=table.getOutcomeFields()> | |
<table> | |
<thead><tr> | |
<#list fields as field> | |
<th>${field?html}</th> | |
</#list> | |
</tr></thead> | |
<tbody> | |
<#list outcomes as outcome> | |
<#assign isVerified=outcome.isVerified()?string> | |
<#if isVerified == "true"> <#assign verified="verified"><#else><#assign verified="notVerified"></#if> | |
<tr class="${verified}"> | |
<td>${outcome.description?html}</td><td><@renderOutcomeValue outcome.getValue() table.getDateFormat()/></td><td>${outcome.matcher?html}</td><td><#if isVerified == "true">${keywords.yes}<#else>${keywords.no}</#if></td> | |
</tr> | |
</#list> | |
</tbody> | |
</table> | |
</#macro> | |
<#macro renderOutcomeValue value dateFormat><#if value?is_date>${value?string(dateFormat)}<#else>${value?html}</#if></#macro> | |
<#macro renderStep step> | |
<#assign formattedStep = step.getFormattedStep(EscapeMode.HTML, "<span class=\"step parameter\">{0}</span>")> | |
<div class="step ${step.outcome}">${formattedStep}<#if step.getTable()??> <span class="step parameter"><@renderTable step.getTable()/></span></#if> <@renderStepOutcome step.getOutcome()/></div> | |
<#if step.getFailure()??><pre class="failure">${step.failureCause?html}</pre></#if> | |
<#if step.getOutcomes()??> | |
<div class="outcomes"><@renderOutcomes step.getOutcomes()/> | |
<#if step.getOutcomesFailureCause()??><pre class="failure">${step.outcomesFailureCause?html}</pre></#if> | |
</div> | |
</#if> | |
</#macro> | |
<#macro renderStepOutcome outcome><#if outcome=="pending"><span class="keyword ${outcome}">(${keywords.pending})</span></#if><#if outcome=="failed"><span class="keyword ${outcome}">(${keywords.failed})</span></#if><#if outcome=="notPerformed"><span class="keyword ${outcome}">(${keywords.notPerformed})</span></#if></#macro> | |
<html> | |
<body> | |
<div class="story"><h1><@renderMultiline story.getDescription()/></h1> | |
<div class="path">${story.path}</div> | |
<#if story.getMeta()??><@renderMeta story.getMeta()/></#if> | |
<#if story.getNarrative()??><@renderNarrative story.getNarrative()/></#if> | |
<#assign scenarios = story.getScenarios()> | |
<#list scenarios as scenario> | |
<div class="scenario"><h2>${keywords.scenario} <@renderMultiline scenario.getTitle()/></h2> | |
<#if scenario.getMeta()??><@renderMeta scenario.getMeta()/></#if> | |
<#if scenario.getGivenStories()??><@renderGivenStories scenario.getGivenStories()/></#if> | |
<#if scenario.getExamplesTable()??> | |
<div class="examples"><h3>${keywords.examplesTable}</h3> | |
<#list scenario.getExamplesSteps() as step> | |
<div class="step">${step?html}</div> | |
</#list> | |
<@renderTable scenario.getExamplesTable()/> | |
</div> <!-- end examples --> | |
<#if scenario.getExamples()??> | |
<#list scenario.getExamples() as example> | |
<h3 class="example">${keywords.examplesTableRow} ${example?html}</h3> | |
<#assign steps = scenario.getStepsByExample(example)> | |
<#list steps as step> | |
<@renderStep step/> | |
</#list> | |
</#list> | |
</#if> | |
<#else> <!-- normal scenario steps --> | |
<#assign steps = scenario.getSteps()> | |
<#list steps as step> | |
<@renderStep step/> | |
</#list> | |
</#if> | |
</div> <!-- end scenario --> | |
</#list> | |
<#if story.isCancelled()?string == 'true'> | |
<div class="cancelled">${keywords.storyCancelled} (${keywords.duration} ${story.storyDuration.durationInSecs} s)</div> | |
</#if> | |
</div> <!-- end story --> | |
<#if story.getPendingMethods()??> | |
<#list story.getPendingMethods() as method> | |
<div><pre class="pending">${method?html}</pre></div> | |
</#list> | |
</#if> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Scenario: a trade with a positive price passes validation | |
Given a trade with a positive price | |
When the trade is validated | |
Then trade passes validation | |
Scenario: a trade with a negative price fails validation | |
Given a trade with a negative price | |
When the trade is validated | |
Then trade fails validation | |
Scenario: a scenario with a table | |
Given a table: | |
|header1 |header2 |header3 |header4 |header5 |header6 |header7 | | |
|value1 |value2 |value3 |value4 |value5 |value6 |value7 | | |
Then a table: | |
|header1 |header2 |header3 |header4 |header5 |header6 |header7 | | |
|value1 |value2 |value3 |value4 |value5 |value6 |value7 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beans xmlns="http://www.springframework.org/schema/beans" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:context="http://www.springframework.org/schema/context" | |
xmlns:aop="http://www.springframework.org/schema/aop" | |
xsi:schemaLocation=" http://www.springframework.org/schema/aop | |
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> | |
<context:annotation-config /> | |
<context:component-scan base-package="foo.bar"/> | |
<bean id="scenarioScope" class="foo.bar.ScenarioScope"/> | |
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> | |
<property name="scopes"> | |
<map> | |
<entry key="scenario" value-ref="scenarioScope"/> | |
</map> | |
</property> | |
</bean> | |
<bean id="scenarioContext" class="foo.bar.ScenarioContext" scope="scenario"> | |
<aop:scoped-proxy/> | |
</bean> | |
</beans> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment