TDD Unit Testing – How to Test Private Collection Additions

dependenciesdependency-injectionencapsulationtddunit testing

Assume that I planned to write a class that worked something like this:

public class GameCharacter {
    private Collection<CharacterEffect> _collection;

    public void Add(CharacterEffect e) { ... }
    public void Remove(CharacterEffect e) { ... }
    public void Contains(CharacterEffect e) { ... }
}

When added an effect does something to the character and is then added to the _collection. When it is removed the effect reverts the change to the character and is removed from the _collection.

It's easy to test if the effect was applied to the character, but how do I test that the effect was added to _collection?

What test could I write to start constructing this class. I could write a test where Contains would return true for a certain effect being in _collection, but I can't arrange a case where that function would return true because I haven't implemented the Add method that is needed to place things in _collection.

Ok, so since Contains is dependent on having Add working, then why don't I try to create Add first. Well for my first test I need to try and figure out if the effect was added to the _collection. How would I do that? The only way to see if an effect is in _collection is with the Contains function.

The only way that I could think to test this would be to use a FakeCollection that Mocks the Add, Remove, and Contains of a real collection, but I don't want _collection being affected by outside sources. I don't want to add a setEffects(Collection effects) function, because I do not want the class to have that functionality. The one thing that I am thinking could work is this:

public class GameCharacter<C extends Collection> {
    private Collection<CharacterEffect> _collection;

    public GameCharacter() {
        _collection = new C<CharacterEffect>();
    }
}

But, that is just silly making me declare what some private data structures type is on every declaration of the character.

Is there a way for me to test this without breaking TDD principles while still allowing me to keep my collection private?

Best Answer

The confusion you are having here is because you are trying to write a test about the implementation details, not about the desired effect. Thinking instead about the desired behaviors of the class is likely to lead you to a better test.

In this example, it seems to me you have two behaviors you want to test:

  • When added an effect does something to the character
  • When it is removed the effect reverts the change to the character

I'm not exactly sure what a character effect is, but here's an example test that makes a fairly reasonable assumption, and hopefully gets my point across even if it's a little off:

[Test]
public void AddAndRemovingEffectsAltersCharachterAppropriately()
{
    var originalStrength = _character.Strength;
    CharacterEffect strengthPotion = new StrengthPotion();
    _character.Add(strengthPotion);
    Assert.That(_character.Strength, Is.GreaterThan(originalStrength));

    //normally I'd make a second test for this, 
    //but the answer edit box is not a great IDE  :)
    _character.Remove(strengthPotion);
    Assert.That(_character.Strength, Is.EqualTo(originalStrength));
}

Notice: this test doesn't care about the collection at all. It's not obvious from reading it that a collection is necessary. The collection is merely an implementation detail. It's beneath the notice of the test.

Side note: The "add" and "remove" names for methods do suggest a collection, and don't really communicate that domain relevant purpose of the methods. I'd prefer something like Apply or Consume over Add. Or perhaps character.Inventory.Add() if we are talking about equipment.