This is the next part of the Mars Rover Kata. Part 1 can be found here.
In part 1 we used the state pattern to determine the rover’s direction after turning left or right.
Time to start creating the Rover. However before start on the rover we need to create the landscape that the rover will operate in.
[Test] public void When_Create_The_Landscape_That_Rover_Position_Will_Be_0_0() { var landscape = new Landscape(5); Assert.That(landscape.CoordinateX, Is.EqualTo(0)); Assert.That(landscape.CoordinateY, Is.EqualTo(0)); }
Firstly, the interface needs to be updated:
public interface ILandscape { int CoordinateX { get; } int CoordinateY { get; } }
Secondly, is to create a Landscape class that inherits from the ILandscape interface
public class Landscape : ILandscape { public int CoordinateX { get; private set; } public int CoordinateY { get; private set; } private readonly int _gridSize; public Landscape(int gridSize) { CoordinateX = 0; CoordinateX = 0; _gridSize = gridSize; } }
Now the test passes. Now that we have the landscape, its time to start with the rover. Start with the most basic test, when we create the rover, the direction is North.
[Test] public void When_Rover_Is_Created_Start_At_North() { var rover = new Rover(_landscape); Assert.That(rover.Direction, Is.TypeOf<North>()); }
Now time to create the Rover.
public class Rover { private readonly ILandscape _landscape; public IDirection Direction { get; private set; } public Rover(ILandcape landscape) { _landscape = landscape; Direction = new North(_landscape); } }
The next thing that we want to quickly test with the rover, is that we can turn left and right. This is the tests to satisfy that requirement
[Test] public void When_Rover_Is_Created_And_Turn_Left_The_Direction_Is_West() { var rover = new Rover(_landscape); rover.TurnLeft(); Assert.That(rover.Direction, Is.TypeOf<West>()); } [Test] public void When_Rover_Is_Created_And_Turn_Right_The_Direction_Is_East() { var rover = new Rover(_landscape); rover.TurnRight(); Assert.That(rover.Direction, Is.TypeOf<East>()); }
As I have already tested the direction code is part 1, I wont be testing all the turns again.
The updated rover class is now
public class Rover { private readonly ILandscape _landscape; public IDirection Direction { get; private set; } public Rover(ILandscape landscape) { _landscape = landscape; Direction = new North(_landscape); } public void TurnLeft() { Direction = Direction.TurnLeft(); } public void TurnRight() { Direction = Direction.TurnRight(); } }
As before all the tests pass.
What looking to test next is the movement of the rover going forward
[Test] public void When_Rover_Is_Created_Is_Facing_North_And_Move_Rover_Will_Be_At_Coordinates_0_1() { var rover = new Rover(_landscape); rover.Forward(); Assert.That(rover.CoordinateX, Is.EqualTo(0)); Assert.That(rover.CoordinateY, Is.EqualTo(1)); }
To get this test to pass, we need to update the rover to move on the landscape. So we have to start off altering IDirection to include a new void method Move.
On the North class, we have to get the landscape to move the rover forward
public class North : IDirection { public void Move() { _landscape.MoveYForward(); } } public class Landscape : ILandscape { public void MoveYForward() { CoordinateY++; } }
To test the movement when the direction is east, some setup is required. The rover needs to be turned right first before the rover is moved.
[Test] public void When_The_Rover_Is_Created_Turn_Right_And_Move_Rover_Will_Be_At_Coordinates_1_0() { var rover = new Rover(_landscape); rover.TurnRight(); rover.Forward(); Assert.That(rover.CoordinateX, Is.EqualTo(1)); Assert.That(rover.CoordinateY, Is.EqualTo(0)); }
The other option is to stub out the landscape so that the position of the rover does not start at 0, 0 but at say 3, 3 instead, because now I don’t want to worry about moving past the border. Still have to write the tests to ensure that cannot move outside of the boundary of the landscape.
public class East : IDirection { public void Move() { _landscape.MoveXForward(); } } public class Landscape : ILandscape { public void MoveXForward() { CoordinateX++; } }
To get the tests working for moving when the direction is West and South, the same type of setup is required. The final two movement tests are
[Test] public void When_The_Rover_Is_Created_And_Move_So_That_Facing_South_And_Move_Coordinates_Will_Be_0_1() { var rover = new Rover(_landscape); rover.Forward(); rover.Forward(); rover.TurnRight(); rover.TurnRight(); Assert.That(rover.Direction, Is.TypeOf<South>()); rover.Forward(); Assert.That(rover.CoordinateX, Is.EqualTo(0)); Assert.That(rover.CoordinateY, Is.EqualTo(1)); } [Test] public void When_The_Rover_Is_Created_And_Move_So_That_Facing_West_And_Move_Coordinates_Will_Be_1_0() { var rover = new Rover(_landscape); rover.TurnRight(); rover.Forward(); rover.Forward(); rover.TurnRight(); rover.TurnRight(); Assert.That(rover.Direction, Is.TypeOf<West>()); rover.Forward(); Assert.That(rover.CoordinateX, Is.EqualTo(1)); Assert.That(rover.CoordinateY, Is.EqualTo(0)); }
And the changes that are required to the South, West and Landscape classes to get the tests to pass
public class South : IDirection { public void Move() { _landscape.MoveYBackward(); } } public class West: IDirection { public void Move() { _landscape.MoveXBackward(); } } public class Landscape : ILandscape { public void MoveXBackward() { CoordinateX--; } public void MoveYBackward() { CoordinateY--; } }
The final tests that need to perform for the movement is to ensure that for the remaining movement tests the mover cannot move past the boundary.
These are the tests that are required
[Test] public void When_At_Start_And_Facing_South_And_Move_Do_Not_Pass_Boundary() { var direction = new South(_landscape); direction.Move(); Assert.That(_landscape.CoordinateX, Is.EqualTo(0)); Assert.That(_landscape.CoordinateY, Is.EqualTo(0)); } [Test] public void When_At_Start_And_Facing_West_And_Move_Do_Not_Pass_Boundary() { var direction = new West(_landscape); direction.Move(); Assert.That(_landscape.CoordinateX, Is.EqualTo(0)); Assert.That(_landscape.CoordinateY, Is.EqualTo(0)); } [Test] public void When_Moving_North_At_Y_Boundary_Do_Not_Pass_Boundary() { var direction = new North(_landscape); for (int i = 0; i < 10; i++) { direction.Move(); } Assert.That(_landscape.CoordinateX, Is.EqualTo(0)); Assert.That(_landscape.CoordinateY, Is.EqualTo(5)); } [Test] public void When_Moving_East_At_X_Boundary_Do_Not_Pass_Boundary() { var direction = new West(_landscape); direction.Move(); Assert.That(_landscape.CoordinateX, Is.EqualTo(5)); Assert.That(_landscape.CoordinateY, Is.EqualTo(0)); }
To get these tests to pass, have to alter the landscape class
public void MoveXForward() { if (CoordinateX < _gridSize) CoordinateX++; } public void MoveXBackward() { if (CoordinateX > 0) CoordinateX--; } public void MoveYForward() { if (CoordinateY < _gridSize) CoordinateY++; } public void MoveYBackward() { if (CoordinateY > 0) CoordinateY--; }
All that is left to do is work on receiving the commands to move the rover. That will be the topic of part 3 of the Mars Rover Kata.
[…] is the third and final part to solve the Mars Rover kata. Part 1 and Part 2 to view what has been done […]