![]() |
The Secret Ninja Cucumber Scrolls |
|
Putting all our ninjas in a single file is a good trick, made popular by the Black Star Ninja in the famous documentary about the life of Ninjas on Philippines from the 80's, wrongly titled The American Ninja. However, sometimes it is useful to break our step definitions into several files, and then share the context between them. Cucumber supports this with all the platforms, but in different ways. So far, all our steps have been in a single file, corresponding to a feature file. As things get more complex, you'll want to write feature files that reuse some of the steps from other feature files and combine them with other steps, so the mapping between a step definition and a feature file is not necessarily the same as between Subzero and Scorpion. In this section, we'll show aspiring Ninja Cucumberists how to split these apart. To demonstrate that, let's add a new feature file that reuses some of the steps that we already defined and adds some new steps. Paste the
following content in Feature: Split Brains
Ninjas can receive training to withstand a direct hit on the head
Scenario: Samurai katana is useless against a ninja >= 3rd level
Given the ninja has a third level black-belt
When hit on the head by a samurai with a katana
Then the ninja's brains should not be harmed
Scenario: Ninja training is useless against Chuck Norris
Given the ninja has a third level black-belt
When hit on the head by Chuck Norris with a fist
Then the ninja's brains should split The initial step Ruby loads step definitions from any files in the cucumber features/split.feature Cucumber will execute just the new feature and report that it found the first step in Now we can add another step definition that completes the impact analysis required to decide whether the brains of a ninja should not be
harmed or be split after a direct hit. Just to demonstrate that things can be a bit more complex, we'll put this logic into an
impact calculator. The step definition file just needs to read the ninja created by the step in require 'rspec/expectations' require 'cucumber/formatter/unicode' $:.unshift(File.dirname(__FILE__) + '/../../src') require 'ninja' require 'impact_calculator' When /^hit on the head by [a ]*([A-z ]*) with a ([A-z]*)$/ do |opponent, weapon| @opponent=opponent @weapon=weapon end Then /^the ninja's ([A-z]*) should ([A-z]*)$/ do |target,expected_impact| impact_c=ImpactCalculator.new actual_impact=impact_c.impact @ninja,@opponent,@weapon,@target actual_impact.should==expected_impact end Then /^the ninja's ([A-z]*) should not be harmed$/ do |target| impact_c=ImpactCalculator.new actual_impact=impact_c.impact @ninja,@opponent,@weapon,@target actual_impact.should=="not harmed" end We'll leave the ImpactCalculator class to you for homework, or if you want to cheat go and get it from the
Cuke4Ninja github repository[25] - look for a file called Cuke4Duke loads step definitions from any classes annotated with Cucumber annotations such as mvn integration-test Cucumber will execute the new feature file and find some of the steps in the old The first step, from First, let's define a context class. We just need to share the package ninjasurvivalrate;
public class NinjaContext{
private Ninja ninja;
public void setNinja(Ninja ninja){
this.ninja=ninja;
}
public Ninja getNinja(){
return this.ninja;
}
}Now let's change the existing public class NinjaSurvivalSteps {
private NinjaContext ninjaContext;
public NinjaSurvivalSteps(NinjaContext ninjaContext){
this.ninjaContext=ninjaContext;
}At this point, you should be able to run the tests again and not notice any difference in the old functionality. Although the old step definition class did not have a constructor and this one does, and it requires a context parameter, Cuke4Duke wires that in automatically using your selected dependency injection container Now we can add another step definition class with a similar constructor to handle the missing steps: package ninjasurvivalrate;
import cuke4duke.annotation.I18n.EN.Given;
import cuke4duke.annotation.I18n.EN.Then;
import cuke4duke.annotation.I18n.EN.When;
import java.util.Map;
import java.util.List;
import java.util.Collection;
import static junit.framework.Assert.assertEquals;
public class SplitSteps {
private NinjaContext ninjaContext;
private String opponent;
private String weapon;
public SplitSteps(NinjaContext ninjaContext){
this.ninjaContext=ninjaContext;
}
@When ("^hit on the head by [a ]*([A-z ]*) with a ([A-z]*)$")
public void hitOnTheHead(String opponent, String weapon) {
this.opponent=opponent;
this.weapon=weapon;
}
@Then ("^the ninja's ([A-z]*) should not be harmed$")
public void shouldNotBeHarmed(String target) {
expectImpact(target,"not harmed");
}
@Then ("^the ninja's ([A-z]*) should ([A-z]*)$")
public void expectImpact(String target, String expectedImpact) {
String actualImpact=ninjaContext.getNinja().impact(target,opponent,weapon);
assertEquals(expectedImpact,actualImpact);
}
}We'll leave it to you for homework to implement the missing function for impact analysis in the Cuke4Nuke loads step definitions from any classes annotated with Cucumber annotations such as Cucumber will execute the new feature file and find some of the steps in the old The first step, from First, let's define a context class. We just need to share the Ôªønamespace NinjaSurvivalRate
{
public class NinjaContext
{
public Ninja Ninja{get; set;}
}
}Now let's change the existing public NinjaSteps(NinjaContext ninjaContext)
{
_ninjaContext = ninjaContext;
}At this point, you should be able to run the tests again and not notice any difference in the old functionality. Although the old step definition class did not have a constructor and this one does, and it requires a context parameter, Cuke4Nuke wires that in automatically using your selected dependency injection container Now we can add another step definition class with a similar constructor to handle the missing steps: Ôªøusing Cuke4Nuke.Framework;
using NUnit.Framework;
namespace NinjaSurvivalRate
{
public class SplitSteps
{
private string _opponent;
private string _weapon;
private readonly NinjaContext _ninjaContext;
public SplitSteps(NinjaContext ninjaContext)
{
_ninjaContext = ninjaContext;
}
[When(@"^hit on the head by [a ]*([A-z ]*) with a ([A-z]*)$")]
public void HitOnHead(string opponent, string weapon)
{
_opponent = opponent;
_weapon = weapon;
}
[Then(@"^the ninja's ([A-z]*) should ([A-z]*)$")]
public void ExpectImpact(string target, string expectedmpact)
{
string actualImpact = _ninjaContext.Ninja.CalculateImpact(_opponent);
Assert.AreEqual(expectedmpact, actualImpact);
}
[Then(@"^the ninja's ([A-z]*) should not be harmed$")]
public void NinjaNotHarmed(string target)
{
ExpectImpact(target, "not harmed");
}
}
}We'll leave it to you for homework to implement the missing function for impact analysis
in the |