2009-04-27
One of my TDD weaknesses: mock objects for complex objects
I am not really tuned in to test driven development yet, although I've written some programs that way (and found them quite valuable). As a result, I have some weaknesses, some TDD things that I just haven't acclimatized to yet. One of them is mock objects, specifically mock versions of complex objects; I don't like them and thus I don't use them. In trying to understand my dislike of them, I think that they seem too complex and fragile for at least two reasons.
First, how do I know that I've got the behavior of the mock objects correct? By definition, the complex objects generally have complex behavior, which can easily mean that the mock versions also need relatively complex behavior, which opens up the possibility of bugs. Second, how do I know that the mock objects are still behaving the same as the real objects? If I change the behavior of the real ones (complete with changing their unit tests to match), I may or may not remember the mock versions, and I may or may not realize that the change I made should change the mock version's responses in some way.
Both of these make me feel that mock versions of complex objects are fragile. I don't like fragile tests; they're a recipe for problems and frustrations, and at least in my state of TDD awareness, frustrations can easily lead to abandoning tests entirely. My current solution is to use more or less real objects but to have a unittest test ordering that matches the bottom up dependencies of the program. If early unittests fail, I know that it is pointless to go on; higher levels of code would just see a whole series of cascade failures as things malfunction underneath them.
(It is possible that part of my problem here is that I am confusing unit tests with some other sort of testing, functional or integration or end to end tests, as a result of having only an informal exposure to TDD.)
2009-04-19
Sometimes you don't want behavior with your data
I have heard it said that objects are data combined with behavior, which as a snappy summary strikes me as pretty useful; something like it has certainly guided me in structuring my programs. (Although I am nowhere nearly as strongly object-oriented as is popular these days; I still have at least a foot in the procedural programming camp.)
It is also a sensible notion, in the abstract; since data by itself is useless, so you might as well bundle it together with the code that understands it and call them one thing. (You usually can't manipulate the data without understanding it, so you just put all of the code that understands the data into one place. Phrased this way, one may begin to see some exceptions.)
However, I've come to realize that sometimes this is wrong; sometimes you actively want data without behavior dragged along with it, so you want pure data, not objects. The obvious case is (data) persistence, where I think that you are significantly better off if you do not try to tightly bind data and behavior when you save and restore the data. Here the important thing is usually the data, and you do want to be able to get at it without the behavior intervening.
(I think that part of this is because the behavior may change over time in various ways without the data itself changing.)
(This is probably obvious to experienced OO people, but I learned programming in the pre-OO days so I am still sort of feeling my way through all of this.)