As part of setting up acceptance testing for my robocode robot, I need to be able to run a battle from each acceptance test that I write and I therefore need to create some code that will facilitate this. I've got an idea about how I want this to take shape:
Battle - a class that holds convention led battle information from a name provided by the acceptance test
BattleRunner - a class that will run a particular battle
BattleReader - a class that will read the results of a given battle
BattleResult - a class representing an individual robot's result in the battle
So I start as usual with a specification:
public class when_creating_a_battle { Establish context = () => battleName = "mk77askdnoasid98asd"; Because of = () => battle = new Battle(battleName); It should_contain_the_battle_name_it_was_created_with = () => battle.BattleName.ShouldEqual(battleName); static Battle battle; static string battleName; }
This is very straight forward:
public class Battle { public Battle(string battleName) { BattleName = battleName; } public string BattleName { get; private set; } }
I now want this battle file to derive the battle file name by convention from the battle name provided - I've made the BattleFile property a System.Uri and I assert it contains the relevant parts :
public class when_creating_a_battle { Establish context = () => { battleName = "mk77askdnoasid98asd"; expectedPartOfTheBattleFile = string.Format("{0}.battle", battleName); }; Because of = () => battle = new Battle(battleName); It should_contain_the_battle_name_it_was_created_with = () => battle.BattleName.ShouldEqual(battleName); It should_by_convention_create_the_file_name_representing_this_battle = () => battle.BattleFile.LocalPath.ShouldContain(expectedPartOfTheBattleFile); It should_use_the_known_battle_files_location_when_creating_the_battle_file = () => battle.BattleFile.LocalPath.ShouldContain(BattleConstants.BattleFilesLocation); static Battle battle; static string battleName; static string expectedPartOfTheBattleFile; }
And again, straightforward implementation:
public class Battle { public Battle(string battleName) { BattleName = battleName; BattleFile = new Uri( string.Format(@"{0}{1}.battle", BattleConstants.BattleFilesLocation, battleName)); } public string BattleName { get; private set; } public Uri BattleFile { get; private set; } } public static class BattleConstants { public const string BattleFilesLocation = @"C:\dev\Projects\RoboCodeWars\TeamSlayer\Specifications\AcceptanceSpecs\battles\"; }
I now want to do the same for the results file location:
public class when_creating_a_battle { Establish context = () => { battleName = "mk77askdnoasid98asd"; expectedPartOfTheBattleFile = string.Format("{0}.battle", battleName); expectedPartOfTheResultFile = string.Format("{0}.result", battleName); }; Because of = () => battle = new Battle(battleName); It should_contain_the_battle_name_it_was_created_with = () => battle.BattleName.ShouldEqual(battleName); It should_by_convention_create_the_file_name_representing_this_battle = () => battle.BattleFile.LocalPath.ShouldContain(expectedPartOfTheBattleFile); It should_use_the_known_battle_files_location_when_creating_the_battle_file = () => battle.BattleFile.LocalPath.ShouldContain(BattleConstants.BattleFilesLocation); It should_by_convention_create_the_file_name_representing_the_result = () => battle.BattleResultFile.LocalPath.ShouldContain(expectedPartOfTheResultFile); It should_use_the_known_battle_results_file_location_for_the_results_file = () => battle.BattleResultFile.LocalPath .ShouldContain(BattleConstants.BattleResultsLocation); static Battle battle; static string battleName; static string expectedPartOfTheBattleFile; static string expectedPartOfTheResultFile; }
The implementation is:
public class Battle { public Battle(string battleName) { BattleName = battleName; BattleFile = new Uri( string.Format(@"{0}{1}.battle", BattleConstants.BattleFilesLocation, battleName)); BattleResultFile = new Uri( string.Format(@"{0}{1}.result", BattleConstants.BattleResultsLocation, battleName)); } public string BattleName { get; private set; } public Uri BattleFile { get; private set; } public Uri BattleResultFile { get; private set; } }
Now I want to move onto the battle runner and having already spiked this at an earlier stage, I know I need to prepare data for starting a process. So I'll begin with introducing an interface for a process runner. I start with a new specification:
public class when_the_battle_runner_runs_a_battle { Establish context = () => { processRunner = MockRepository.GenerateStub<IProcessRunner>(); battleRunner = new BattleRunner(processRunner); string battleName = "p4389y78asdj"; battle = new Battle(battleName); }; Because of = () => battleRunner.Run(battle); It should_tell_the_process_runner_to_run_a_new_battle_process = () => processRunner.AssertWasCalled(p => p.RunProcess(Arg<ProcessStartInfo>.Is.NotNull)); static BattleRunner battleRunner; static IProcessRunner processRunner; static Battle battle; }
Implementation:
public interface IProcessRunner { void RunProcess(ProcessStartInfo processStartInfo); } public class BattleRunner { readonly IProcessRunner processRunner; public BattleRunner(IProcessRunner processRunner) { this.processRunner = processRunner; } public void Run(Battle battle) { processRunner.RunProcess(new ProcessStartInfo()); } }
Now I want to record the information that is sent in on that process start information, so I stub the method and use the Do() syntax to record (this is my personal preference and hope to blog about my thoughts on Do(), WhenCalled() and GetArgumentsForCallsMadeOn() at a later date ).
public class when_the_battle_runner_runs_a_battle { Establish context = () => { processRunner = MockRepository.GenerateStub<IProcessRunner>(); processRunner.Stub(p => p.RunProcess(null)) .IgnoreArguments().Do(RecordProcessStartInfoUsedInCall); battleRunner = new BattleRunner(processRunner); string battleName = "p4389y78asdj"; battle = new Battle(battleName); }; static Action<ProcessStartInfo> RecordProcessStartInfoUsedInCall = s => processStartInformation = s; Because of = () => battleRunner.Run(battle); It should_tell_the_process_runner_to_run_a_new_battle_process = () => processRunner.AssertWasCalled(p => p.RunProcess(Arg<ProcessStartInfo>.Is.NotNull)); It should_include_the_known_battle_runner_batch_file_in_the_start_arguments = () => processStartInformation.FileName .ShouldEqual(BattleConstants.BattleFilesLocation + "run_battle.bat"); It should_set_the_working_directory_for_robocode = () => processStartInformation.WorkingDirectory .ShouldEqual(BattleConstants.RobocodeWorkingDirectory); It should_include_the_battle_in_the_start_arguments = () => processStartInformation.Arguments.ShouldContain("-battle " + battle.BattleFile.LocalPath); It should_include_the_results_in_the_start_arguments = () => processStartInformation.Arguments .ShouldContain("-results " + battle.BattleResultFile.LocalPath); static BattleRunner battleRunner; static IProcessRunner processRunner; static Battle battle; static ProcessStartInfo processStartInformation; }
And below is the working code so far. I'll continue the development of the battle classes in my next robocode post.
public class BattleRunner { readonly IProcessRunner processRunner; public BattleRunner(IProcessRunner processRunner) { this.processRunner = processRunner; } public void Run(Battle battle) { var startInformation = new ProcessStartInfo(); startInformation.FileName = BattleConstants.BattleFilesLocation + "run_battle.bat"; startInformation.WorkingDirectory = BattleConstants.RobocodeWorkingDirectory; startInformation.Arguments = string.Format("-battle {0} -results {1}", battle.BattleFile.LocalPath, battle.BattleResultFile.LocalPath); processRunner.RunProcess(startInformation); } }
0 comments: