> How do you test an objects private inner objects?
I have read with interest the differnt answers to this question - we
grappled with this for a while and did a brainstorming session on it (see:
http://www.xpdeveloper.com/cgi-bin/wiki.cgi?LegMeetingSep1999).
We looked at many of the answers given so far - and didn't really like any
of them - they all feel stange. Making everything public or adding
setters/getters just for testing just feels funny (it breaks encapsulation
even for a savy XP team - and I have come around to the thinking that
privacy does add extra documentation). Putting friend classes in the same
package also seems unsatisfactory as well.
Anyway - we took a clean sheet, went back to the tests and asked what
were the kinds of tests we wanted to write where you didn't have these
issues. I was also concerned about about how many of our tests seemed to
test things where we had lost the precision of the originial objects (e.g.
parsing strings when I knew that several calls back we had the original
object that I could have asserted things against).
After discussing for a bit - we noticed that a Mock Type solution seemed to
satisify many of our criteria - and after using it for a bit we have started
to notice patterns emerging.
If you write all of your code in an XP way then you always write the
test fixtures up front. We have started to notice that if you are
adding a feature to a model that requires a relationship to another (new)
model, starting out with
a MockType replacement and testing from the perspective of the existing
model
is quite a good way to start. This is similar to what many people already do
when they create a model stub and then turn that stub into real code. We
have found however, that keeping the stub around as a MockType is very
useful, as the MockType can be used in many other situations and it
maintains its original stub simplicity and can maintain test assertions.
Thus you test your
existing model with a Mock and you use that Mock as an interface for what
you really want. Your tests will guide the shape of that MockType until you
have learned enough to write the real Object.
In contrast - all of the times we have identified another object that we
needed and have concentrated on the tests for that new object (temporarily
ignoring its relationship to the other object) we have usually written the
wrong tests, or added tests for functionality we didn't need.
Taking this approach however, changes the way you write your code (e.g. the
tests subtly change the model code you write - which is what Ward and Ron
keep mentioning). You start to pass MockTypes into your model, so that
you can assert conditions or answer known values.
At the point we have the MockType working in place with the existing object,
we then factor out the interface from the Mock, use that interface to
generate the real object with no implementation (this is easy in VisualAge)
and then create a text fixture for the new object. We write tests for the
funcationaly that the Mock was providing until they all pass. At this point
we go back to the initial model and replace its reference from using a mock
to using the new tested type that we have created. We rerun the tests and
everything should work as before.
Finally there may be one last funtional (or system test) that proves the
linkages are correct - sometimes there is a place where you actually create
the type instances so they get pased down into the model.
When you have done all of this and take a step back, you start to notice
that you are obeying the laws of Dememter - which is actually quite a
strange feeling - the Tests are helping you write code that you thought was
too awkward to write.
e.g. in the GOF wordprocessing example where they have a compositor strategy
(forget about the patterns for a moment). If you wanted to test that your
wordprocessor composed some text the right way, what would you want to do? I
would want to setup my wp object with some fake compositor and make sure it
was at least called once, and that the result that I sent back produced the
correct result.
You should be able to imagine faking this easily, with something that
returns a fixed string (or maybe an array) and then asserting that there are
X lines of text in the WP (or something similar).
The point is you get a lot of mileage from composing objects, but composing
them with MockTypes first so that you can test. In this manner your private
state seems to disapear (at least it does for things that are complicated
enough to warrant testing).
Tim