I've been sharing my experiences in driving my robocode implementation through specification and in my last few posts showed how my thinking has evolved and how I wanted to have some bounded contexts between decision making, calculation and manipulation.
I've got my robot to a stage where it scans once, then does not fire and to make it do more things I need to publish some more state to observables. My existing code needs a refactor to any new shape I derive but I have acceptance tests that assert against that development so I'm free to change it with some degree of confidence.
So I go back to an older specification for the SlayerRobot itself - when the turn status is updated and mark this to be removed at some stage. Here's what it currently looks like:
public class when_robocode_updates_the_turn_status_on_a_robot_TO_BE_REMOVED : with_robot_set_up { Establish context = () => { robot.StartOfEveryTurn += (s, e) => statusAtStartOfTheTurn = e; turnNumber = 34; robotStatus = RobocodeSpy.CreateARobotStatus(turnNumber); statusEvent = new StatusEvent(robotStatus); }; Because of = () => robot.OnStatus(statusEvent); It should_update_the_start_of_every_turn_event_with_the_turn_number = () => statusAtStartOfTheTurn.TurnNumber.ShouldEqual(turnNumber); static StatusEvent statusEvent; static RobotStatus robotStatus; static long turnNumber; static StatusAtTheStartOfATurn statusAtStartOfTheTurn; }
I want a new way of updating the state, so create a new specification:
public class when_robocode_updates_the_turn_status_on_a_robot : with_robot_set_up { Establish context = () => { robotStatus = RobocodeSpy.CreateARobotStatus(0, 2.3); statusEvent = new StatusEvent(robotStatus); robotEngine = MockRepository.GenerateStub<ImARobotEngine>(); serviceLocator.Stub(x => x.ProvideRobotEngine()).Return(robotEngine); robot.Run(); }; Because of = () => robot.OnStatus(statusEvent); It should_tell_the_robot_engine_to_publish_to_the_world_state = () => robotEngine.AssertWasCalled(x => x.PublishToTheWorldState(Arg<Action<IWorldState>>.Is.NotNull)); static StatusEvent statusEvent; static RobotStatus robotStatus; static ImARobotEngine robotEngine; }
I've first expanded my RobocodeSpy class to allow the gun heat to be injected when creating a RobotStatus - here I'm setting it to 2.3. I then provide a stubbed RobotEngine by using the service locator and run the robot to begin that initialisation.
When the event is called, I now want the robot to publish to the world state through the robot engine - it can provide an Action<IWorldState> that the robot engine will take care of.
The specification fails, so I implement like this (only relevant part of the robot code is shown):
public class SlayerRobot : Robot, ImARobot { public override void OnStatus(StatusEvent e) { _robotEngine.PublishToTheWorldState(w => { }); } }
I'm publishing to the world state and my specification passes. I now want to verify that the turn number and the gun heat are updated on the world state so I add some more assertions:
public class when_robocode_updates_the_turn_status_on_a_robot : with_robot_set_up { Establish context = () => { expectedTurnNumber = 32; expectedGunHeat = 2.3; robotStatus = RobocodeSpy.CreateARobotStatus(expectedTurnNumber, expectedGunHeat); statusEvent = new StatusEvent(robotStatus); robotEngine = MockRepository.GenerateStub<ImARobotEngine>(); robotEngine.Stub(x => x.PublishToTheWorldState(null)) .IgnoreArguments().Do(RecordPublicationToTheWorldState); serviceLocator.Stub(x => x.ProvideRobotEngine()).Return(robotEngine); robot.ServiceLocator = serviceLocator; robot.Run(); }; Because of = () => robot.OnStatus(statusEvent); It should_tell_the_robot_engine_to_publish_to_the_world_state = () => robotEngine.AssertWasCalled(x => x.PublishToTheWorldState(Arg<Action<IWorldState>>.Is.NotNull)); It should_affect_the_turn_number_on_the_world_state = () => TheWorldState.CurrentTurnNumber.ShouldEqual(expectedTurnNumber); It should_affect_the_gun_temperature_on_the_world_state = () => TheWorldState.GunTemperature.ShouldEqual(expectedGunHeat); static Action<Action<IWorldState>> RecordPublicationToTheWorldState = a => updateTheWorldState = a; protected static IWorldState TheWorldState { get { var worldState = MockRepository.GenerateStub<IWorldState>(); updateTheWorldState(worldState); return worldState; } } static StatusEvent statusEvent; static RobotStatus robotStatus; static ImARobotEngine robotEngine; static Action<IWorldState> updateTheWorldState; static long expectedTurnNumber; static double expectedGunHeat; }
There are quite a few changes here - first I'm stubbing the robot engine publishing to world state method and am recording the action that is sent in with my RecordPublicationToTheWorldState static field.
I have two new observations that check the world state - each time the property is accessed I create a new stub and apply the recorded action's changes to it and return it.
I can then assert that the robot is indeed suggesting an update to the right areas of the world state.
Here's the implementation to make this pass:
public class SlayerRobot : Robot, ImARobot { public override void OnStatus(StatusEvent e) { _robotEngine.PublishToTheWorldState(w => { w.CurrentTurnNumber = e.Status.Time; w.GunTemperature = e.Status.GunHeat; }); } }
In the next robocode post I'll move onto the robot engine and ensuring the world state updates occur.
0 comments: