Let's recap it from the user perspective:
Let's reword this focusing on the domain model and using its terminology:
CurrentPoints
can be replenished by consuming food
The code shown below is focused on the current cycle step, thus only showing the relevant lines .
The complete code and the Unity project is on GitHub.
The domain model focus will be a new method named Replenish
.
One case is if the player decides to make the Avatar
use an item that replenishes when Health is full,
a complete waste of an item as CurrentPoints
don't increase,
but a valid case regardless.
// HealthTest.cs
// inside nested class Replenish
[Test]
public void CurrentPoints_WhenFullHealth()
{
var health = new Health(12);
health.Replenish(1);
Assert.That(health.CurrentPoints, Is.EqualTo(12));
}
I effortlessly pass the test by adding an empty Replenish
method.
// Health.cs
public void Replenish(int replenishPoints) { }
Of course this will not hold for other cases,
so I'll cycle in more test cases to drive the implementation.
Let's do a test for a more sensible valid case,
when replenish will actually increase the CurrentPoints
.
// HealthTest.cs
// inside nested class Replenish
[Test]
public void CurrentPoints_WhenNotFullHealth()
{
var health = new Health(12);
health.TakeDamage(2);
health.Replenish(1);
Assert.That(health.CurrentPoints, Is.EqualTo(11));
}
I use the existing method TakeDamage
to decrease the CurrentPoints
before the method Replenish
is invoked (the entry point of this test).
(TakeDamage
has already been tested and thus will work as expected).
// Health.cs
public void Replenish(int replenishPoints)
{
CurrentPoints = Math.Min(replenishPoints + CurrentPoints, FullPoints);
}
I'm confident that this is the generic solution for any valid input
so don't add more tests cases.
But I have to handle invalid input, so let's do a cycle for those.
As this will be similar to the 'take damage' handling,
I copy the existing 'take damage throws error' test
and paste it under the nested Replenish
class
and change to 'replenish' appropriately.
// HealthTest.cs
// inside nested class Replenish
[TestCase(0)]
[TestCase(-1)]
public void ThrowsError_WhenReplenishPointsIsInvalid(int replenishPoints)
{
var health = new Health(12);
var exception = Assert.Throws(Is.TypeOf<ArgumentOutOfRangeException>(),
delegate
{
health.Replenish(replenishPoints);
});
Assert.That(exception.Message, Does.Match("invalid").IgnoreCase);
}
This test case 'copy / paste' is valid
as it's tests the same handling for a different entry point and input.
I invoke the existing ValidatePoints
method
passing in replenishPoints
and 1
for lowestValidValue
// Health.cs
public void Replenish(int replenishPoints)
{
ValidatePoints(replenishPoints, 1); // method not shown
CurrentPoints = Math.Min(replenishPoints + CurrentPoints, FullPoints);
}
Now the Unity Test Runner looks like this: