Learning Unit Testing VII – Running and Redesigning

I can see this page 10 being a pretty big thorn in my side. After stating fairly clearly

models can move up to their move rate

under the ‘Moving’ heading, it then says

A running fighter can move at double speed: 8″ rather than 4″, for example.

This is going to have a fairly large impact on the tests involved. The test for exceeding total movement (both in one and two moves) will only throw an exception if the total movement is double the model’s Movement rate now. In addition, a new test is going to have to be added to ensure that moving over the Movement rate sets an ‘IsRunning’ flag to true. The first test to add would be one that allows a model to move more than it’s Movement rate, but sets the IsRunning flag.

[TestMethod]
public void MoveModel_MovesDistanceOverMovementRateAndSetsIsRunningFlag()
{
Model testModel = new Model();
testModel.Movement = 4;
testModel.PositionX = 0;
testModel.PositionY = 0;
testModel.PositionZ = 0;
float newX = 5;
float newY = 4;
float newZ = 3;
testModel.MoveModel(newX, newY, newZ);
Assert.IsTrue(testModel.IsRunning);
}

Even with the correct code added to MoveModel –

if (this.TotalDistanceMoved > this.Movement)
{
this.IsRunning = true;
}

– this doesn’t work. Because ValidateMove is throwing an exception (as we designed in earlier). So ValidateMove has to allow up to twice the MovementRate.

if (GetDistanceFrom(positionX, positionY, positionZ) + this.TotalDistanceMoved > this.Movement * 2)
{
throw new ArgumentException("The model cannot move further than it's Movement rate.");
}

Now, all the tests pass. But should they be? Checking my working, the MoveModel_CannotMoveFurtherThanModelsMovement test is moving the model more than twice it’s movement rate. So it is correct to throw an exception, and therefore pass the test. Anything testing to stay beneath the model’s Movement rate is obviously going to be beneath it’s doubled Movement rate, so they will continue to pass. MoveModel_TotalMovementInASingleTurn… that tests whether two movements that combined make more than the model’s Movement is also working correctly. But I’ve noticed I had an error in that test:

[TestMethod]
public void MoveModel_TotalMovementInASingleTurnCannotExceedModelMovement_ThrowsArgumentException()
{
bool correctExceptionThrown = false;
try
{
Model testModel = new Model();
testModel.Movement = 4;
testModel.PositionX = 0;
testModel.PositionY = 0;
testModel.PositionZ = 0;
float newX = 5;
float newY = 4;
float newZ = 3;
testModel.MoveModel(newX, newY, newZ);
testModel.MoveModel(0, 0, 0);
}
catch (ArgumentException ex)
{
correctExceptionThrown = true;
}
Assert.IsTrue(correctExceptionThrown);
}

The try catch was working on both MoveModel calls – and those values (after the calculation fix a couple of posts back) would have sent it way over the Movement on the first move, not the second. Moving the try…catch so that it only covers the second call to MoveModel would check that it’s failing in precisely the right place. By reversing my change to ValidateMove, I can confirm that the test would have failed before if the try…catch was in the right place. Putting it back in, and it throws an exception on the second MoveModel call, just as it should do. All the tests pass, and I’m a little more comfortable about them testing the right thing.

Lesson learned: Only test exactly the line you expect to break things.

The rest of the ‘Running’ section mentions not being able to shoot – this is something that we can cover in the Shooting section (after Movement), and isn’t important right now. The next bit that should matter is that a running model must stop 8 units away from an enemy model that he can see. This brings a number of things into play immediately:

  1. Can we identify models as being enemies?
  2. Can we identify which models can see others?
  3. How do we perform the sight checks all along it’s movement to work out if it can see the enemy (they may not be in sight at the beginning of the move, only halfway along it)?
  4. What action should be taken if the model’s movement is disrupted – should it still count as running? How can it be prevented from moving further?

Before we write tests, we should explore the problem a bit further.

Essentially, the enemy is at the centre of a circle 16 units in diameter (radius of 8). The active model’s path may intersect the circle at some point. If it does so, the active model must stop at the point along that path nearest to his starting point, providing that he can draw a line of sight to the enemy model at the centre of the circle.

So we only really need to work out if any point of the active model’s path intersects the enemy ‘disruption’ circles, and check those sections of the path for line-of-sight to the centre of the circle.

Leave a comment

Your email address will not be published. Required fields are marked *