8

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?

2
  • TDD is a great way to go, And most of the time your perfectly fine doing it, but there are cases where it doesn't make sense or its just more effort then its worth. As with any tool you have to weigh the cost of using it against the benefits it gives you. If it takes you 5 hours to figure out how to write a test, or rewrite your code to make it pass the test, but you could manually test the same functionality in 4-5min, then cost > benefit imho, and its not worth it.
    – Ryan
    Nov 16, 2012 at 21:40
  • 3
    @ryanOptini: TDD isn't about testing, it's about design. Don't ever use the "I can test this manually faster" excuse to not bother.
    – pdr
    Nov 16, 2012 at 21:48

5 Answers 5

12

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.

3
  • 1
    +1 for your first paragraph, though I admit the rest confuses me more than it helps. Perhaps if you made the example more concrete, it would better serve to explain your first paragraph. Nov 17, 2012 at 8:43
  • Thanks for the feedback @MarjanVenema. I was hesitant to do that at first, because I had to make some guesses about what a character effect meant in the game, but I think it does add to the point nicely now that I see it. Does it make better sense to you now?
    – tallseth
    Nov 17, 2012 at 13:13
  • Yep. It sure does. Nov 17, 2012 at 18:30
5

You seem to understand the incremental part of TDD, that is to implement the smallest possible functionality at a time. However, there are cases like the one you are facing that make it extremely difficult to do. In your particular situation, I would not bother trying to implement them separatly and I would implement Add and Contains at the same time.

The typical TDD cycle starts with : Write your test and make it compile. In your case, your test would look like so :

var g = new GameCharacter();
var c = new CharacterEffect();
g.Add(c);
Assert.That(g.Contains(c));

In order to make your test compile (step 2), you have to implement both Add and Contains, which I think is perfectly fine. TDD says that you should take the smallest step possible, and this seems to be the smallest you can do. You don't need to go crazy about implementing every method individually or you will likely hit the same walls again and again.

1
  • 1
    +1 for getting the point better than I did. But, without being able to set up a pre-initialised collection, the test for Remove is going to be conditional on both Contains AND Add working. That's less acceptable.
    – pdr
    Nov 16, 2012 at 22:12
5

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?

You shouldn't.

You need to ask why it matters that an item is added to the collection. "Well, it should then have a certain effect on the character." Then test for that effect. There isn't any value in testing that a rudimentary collection works as a collection.

And suppose someday you modify the program to create character effects in a very different way. If you test that your collection works, your test becomes useless when you get rid of the collection. But if you test for character effects, your test will still work with a different code mechanism. That's as it should be; you need to test that your program works correctly, not that your programming language works correctly.

Have a look at this answer, by Kent Beck, about what one should test:

How deep are your unit tests?

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence...If I don't typically make a kind of mistake (like setting the wrong variables in a constructor), I don't test for it.

Unless you're completely unfamiliar with the programming language you're using or with a library you're using, it just doesn't make sense to test that a collection works as a collection, or that you can create a new object, or that a variable is incremented when you tell it to increment.

All you're doing in such cases is making sure your programming language works as it's designed to, which is generally not a thing you'd have to test unless you're the one designing the language.

0

You shouldn't test classes but interfaces where by interface, beside the typical meaning, you could also understand the signatures of the public methods of a class. Your classes should adhere to strong contracts; from the testing point of view (and from other clients' POV) you shouldn't care how those contracts are respected/implemented.

In your particular case there is a problem with your class design; your add method does two things: firstly it changes the CharacterEffect and then it adds that object to the collection of character effects. This bad design decision makes it difficult to test!

If you really want to see a private field, you can use reflection, but that is not an orthodox way of doing things.

0

Have your unit tests be implemented in a friend class.

That lets you poke into private and protected stuff freely for the purposes of the unit test without having to make it part of the external API.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.