Over the past four years I have at various times over and over explained to my co-developers why writing tests against setters and getters is bad, dealt with the whole “metrics” argument, and quite frankly I start to get weary repeating myself every three months.
While I was driving into work today, I started thinking about the subject again as I’ve been debating a topic yet again recently that I quite honestly don’t think it warrants any debate and decided that since I haven’t written any blog posts on the subject, I should. So ahead I will describe why test driving setters and getters isn’t only a waste of time, but it is also considered harmful to your design as well!
First off, I’ll point out why I find it a waste of time. I’ve seen developers labor furiously in the past to verify that a behavior that at the simplest is already done for them by their IDE. If your IDE has generated them for you, and you decide to write tests against it, you’re first of all bullshitting yourself that you’re practicing TDD, because you’re really writing tests after the fact. Secondly, what the frak are you testing? That the developers of intelliJ/eclipse/textmate/whatever wrote their setter/getter generation code correctly?
Another myth is that, while the above argument is correct, the setter/getter “might” have some behavior in it, or if you don’t write a test against it and someone goes in and modifies the behavior, there will be no “tests” to catch it. Now, I can’t say for you, but I don’t write tests to “catch bugs”, I do it both to prevent them and to ensure my design is sound. Secondly, I also adhere to concepts like YAGNI (You Ain’t Gonna Need It) and creating “The Simplest Thing That Can Possibly Work.” If I’m going to change the behavior of my setters or getters, I’ll write a test for that behavior before I change it. If you’re trying to prevent developers from being sneaky, well then you have team issues that you need to address, and I don’t think the best way to deal with those team issues is by decreeing that all setters and getters need to be tested.
These are all perfectly good reasons not to test setters and getters in my humble opinion, but now let’s get to the real reason you shouldn’t do it, and why I even considered it harmful to your design. See, when we practice Test Driven Development, there really shouldn’t be so much of an emphasis on “test” but rather on “design” (this is why I really lean toward using the term Behavior Driven Development). I’ve said this before, but let me recap.
We use tests to drive our design. Each test provides an example of functionality that we use to prove the need for the code we’re going to write to exist. With a failing test in place, we then write the code that we need to make that test pass, then on to the next. Using this iterative approach, we allow our system to take part in emergent design, proving we really do need the code we’ve written. If we wrote code we didn’t write an example for, there’s the possibility that we really don’t need that feature (or we do, we just thought too quickly and didn’t write an example). Setters and Getters should get exercised while executing these examples.
Wait, back up. “Setters and Getters should be exercised while executing these examples.” What I mean by this is that when you write a test against a feature that, let’s say returns an object, you should be doing a verification on that return value’s field to see if a value was set to the expected value, in which case in the course of that test the setter and getter get executed. The example for that specific feature proves that you need that field to hold some value.
A simple example:
@Test
public void shouldAddDiscountIfTenOrMoreItems(){
Order order = new Order();
order.setItems(add(10).items().thatCost(ITEM_PRICE));
calculator.calculateDiscount(order);
assertEquals((ITEM_PRICE*10)*0.20, order.getTotal();
}
Now in this example, we exercise various setters and getters in Order, because we need them. That’s right, because we need them… our feature that our business cares about needs them in order to work.
Now we lead ourselves to why TDD’ing setters/getters is harmful… by writing tests against setters and getters, we’re saying that we need these properties on an object, but we’re not explaining why. This is akin by starting a project by hitting the ground running and designing the database without no real understanding of how the data will be used. At the least you’ll possibly be defining objects and properties you might night use… at worst, you’ll create a design of objects that aren’t grouped within the domain correctly, which will show itself overtime when you have domain objects and services that, although they work, don’t make a lot of sense.
This is a good middle ground for people concerned with metrics. Although I think that high code coverage is a side effect and not the end goal, if you follow this practice you should achieve optimal code coverage without succumbing to wasting your time testing field names.
UPDATE: Pat Maddox posted a link on the TDD mailing list to an old (but rather insightful) article on why getter and setter methods are evil. I’ll have to say I agree with this quite a bit!