In previous posts I've set up an acceptance testing framework that enables me to run a battle and I'm now ready to write my first acceptance test.
To recap, my acceptance test that I want is:
In a one round battle, fighting against a single robot that does not move, the slayer robot should win.
So here's how I want to write that:
public class in_a_one_round_battle_fighting_against_a_single_robot_that_does_not_move { Establish context = () => { battle = new Battle("in_a_one_round_battle_fighting_against_
a_single_robot_that_does_not_move"); battleRunner = new BattleRunner(new ProcessRunner(),
new BattleResultParser(new BattleResultsFileReader())); }; Because of = () => returnedResults = battleRunner.Run(battle); It should_result_in_the_slayer_robot_winning = () => returnedResults.Where(r => r.RobotName.Contains("SlayerRobot")) .Single() .Position.ShouldEqual(1); static Battle battle; static BattleRunner battleRunner; static IEnumerable<BattleResult> returnedResults; }
Now, I manually create a battle file in the battle directory with the same name and set up the battle:
I'm ready to go with the first acceptance test and when I run it, the battle runner runs the process, the results are parsed and the test fails with the following:
My acceptance test fails because my robot is in second place - the acceptance testing framework works!
I now recognise some areas that I can abstract some of the set up so here's the first process of streamlining as I write a base acceptance specification:
public class acceptance_test { Establish context = () => battleRunner = new BattleRunner(new ProcessRunner(), new BattleResultParser(new BattleResultsFileReader())); Because of = () => { battleName.ShouldNotEqual(string.Empty); //battleName must be set battle = new Battle(battleName); returnedResults = battleRunner.Run(battle); }; protected static string battleName = string.Empty; protected static IEnumerable<BattleResult> returnedResults; static Battle battle; static BattleRunner battleRunner; }
This base acceptance class now enables me to have a very small amount of code to write for an acceptance test:
public class in_a_one_round_battle_fighting_against_a_single_robot_that_does_not_move :
acceptance_test { Establish context = () => battleName =
"in_a_one_round_battle_fighting_against_a_single_robot_that_does_not_move"; It should_result_in_the_slayer_robot_winning = () => returnedResults.Where(r => r.RobotName.Contains("SlayerRobot")) .Single() .Position.ShouldEqual(1); }
I can make this even shorter now by adding in an extension method:
public static class BattleResultCollectionExtensions { public static BattleResult SlayerRobotResult(this IEnumerable<BattleResult> battleResults) { return battleResults.Where(r => r.RobotName.Contains("SlayerRobot")).Single(); } }
Now my acceptance test looks great:
public class in_a_one_round_battle_fighting_against_a_single_robot_that_does_not_move :
acceptance_test { Establish context = () => battleName =
"in_a_one_round_battle_fighting_against_a_single_robot_that_does_not_move"; It should_result_in_the_slayer_robot_winning = () => returnedResults.SlayerRobotResult().Position.ShouldEqual(1); }
I could go further and make the SlayerRobotResult part of the base acceptance, but I'll leave this for now as I'm happy with how the test looks.
I now have a quick acceptance testing framework where I can ensure that I do not regress the overall outcome of certain battle situations and this series of posts illustrated how you can think out of the box a little and make white box acceptance testing possible up front before you develop the main parts of your application.
At a future date we may want to look at doing this in-process as opposed to launching an external process - Pavel and Jason have comments about using a jni4net bridge to perhaps accomplish this and it would be great if an acceptance testing mechanism could be included as part of the robocode distribution.
1 comments: