Create a new MSTest Test Project in Visual Studio if you would like to follow along.
Next, add the code from the Simple Item Inventory post so the tests will successfully build.
Let's verify proper functionality in these cases:
Here is the stubbed out code for our test methods:
[TestClass]
public class InventoryTests
{
[TestMethod]
public void AddItem_ItemIsStackable_Stacks()
{
}
[TestMethod]
public void AddItem_ItemIsStackable_DoesntStack()
{
}
[TestMethod]
public void RemoveItem_PartialStack()
{
}
[TestMethod]
public void RemoveItem_EntireStack()
{
}
[TestMethod]
public void RemoveItem_MoreThanExists_Fails()
{
}
}
This can be done by adding a stackable item multiple times and verifying that it was added to the first slot.
Additionally, it makes sense to verify that the second slot is still empty.
[TestMethod]
public void AddItem_ItemIsStackable_Stacks()
{
Inventory inventory = new Inventory(4);
inventory.AddItem(new Item { ID = 1, Quantity = 1 });
ReadOnlyCollection<Item> items = inventory.GetItems();
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(1, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
inventory.AddItem(new Item { ID = 1, Quantity = 1 });
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(2, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
}
Running this test should succeed.
This will be very similar to the previous test, but the quantity of each stack should be one and we need to verify that second added item exists in the second slot.
[TestMethod]
public void AddItem_ItemIsStackable_DoesntStack()
{
Inventory inventory = new Inventory(4);
inventory.AddItem(new Item { ID = 2, Quantity = 1 });
ReadOnlyCollection<Item> items = inventory.GetItems();
Assert.AreEqual(2, items[0].ID);
Assert.AreEqual(1, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
inventory.AddItem(new Item { ID = 2, Quantity = 1 });
Assert.AreEqual(2, items[0].ID);
Assert.AreEqual(1, items[0].Quantity);
Assert.AreEqual(2, items[1].ID);
Assert.AreEqual(1, items[1].Quantity);
}
This test should also succeed.
Add a stack of 10 items and remove 5.
[TestMethod]
public void RemoveItem_PartialStack()
{
Inventory inventory = new Inventory(4);
inventory.AddItem(new Item { ID = 1, Quantity = 10 });
ReadOnlyCollection<Item> items = inventory.GetItems();
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(10, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
inventory.RemoveItem(new Item { ID = 1, Quantity = 5 });
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(5, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
}
Now let's run this test. Since I never make mistakes, this test should succeed.
Unfortunately it fails. Let's investigate.
Put a breakpoint on this line in the code:
inventory.RemoveItem(new Item { ID = 1, Quantity = 5 });
Right click in the test and select the "Debug Tests" option. Stepping through the test, we can see that the item in the first slot is not "equal" to the item we have requested the removal of.
//Items are not being matched by ID here
if (ItemSlots[i].Equals(item))
According to this article, the default value equality for structs does an equality check on all fields and properties. This will fail in our case since the quantity of 10 in the item slot does not match the quantity of 5 of the argument. For our purposes, we should assume that items with the same ID and durability are the same item. In a production implementation, a better solution should be used to prevent incorrect behavior.
Here is our modified Item class that implements a better equality check.
public struct Item : IEquatable<Item>
{
public int ID; //Can be used to gather information about item
public int Durability;
public int Quantity;
public override bool Equals(object? obj) => obj is Item other && Equals(other);
public bool Equals(Item p) => ID == p.ID && Durability == p.Durability;
public override int GetHashCode() => (ID, Durability).GetHashCode();
public static bool operator ==(Item lhs, Item rhs) => lhs.Equals(rhs);
public static bool operator !=(Item lhs, Item rhs) => !(lhs == rhs);
}
The test should now succeed.
Add a stack of 10 and remove all 10.
[TestMethod]
public void RemoveItem_EntireStack()
{
Inventory inventory = new Inventory(4);
inventory.AddItem(new Item { ID = 1, Quantity = 10 });
ReadOnlyCollection<Item> items = inventory.GetItems();
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(10, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
inventory.RemoveItem(new Item { ID = 1, Quantity = 10 });
Assert.AreEqual(0, items[0].ID);
Assert.AreEqual(0, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
}
Add a stack of 10 and try to remove 11.
[TestMethod]
public void RemoveItem_MoreThanExists_Fails()
{
Inventory inventory = new Inventory(4);
inventory.AddItem(new Item { ID = 1, Quantity = 10 });
ReadOnlyCollection<Item> items = inventory.GetItems();
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(10, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
bool didRemove = inventory.RemoveItem(new Item { ID = 1, Quantity = 11 });
Assert.IsFalse(didRemove);
Assert.AreEqual(1, items[0].ID);
Assert.AreEqual(10, items[0].Quantity);
Assert.AreEqual(0, items[1].ID);
}
Run all tests once more to make sure that our changes did not break previous tests.
They all succeed. This exercise is complete!