Noah Roberts wrote:
I'm using a simple unit test architecture I found in "Thinking in C++".
I just decided I wanted to place some member functions out from public
into a protected scope. Problem is that my tests want to access those
functions...bummer. What have other people done when faced with this
problem or just the general problem of testing functionality
encapsulated in a private manner?
There's a spectrum of answers, depending on whether tests are written before
or after the tested code.
Under pure Test Driven Development, you write failing test cases, then write
the code to pass them. Then you refactor the code to make methods generally
shorter and less redundant.
At refactor time, methods should Extract Method their common code into
private methods that only they call. Tests will still cover these private
methods' behaviors. The original test cases that created these behaviors
will still defend the behaviors against change.
As a module grows under test, programmers might need to add behavior
directly to these private methods. At this time, they can write public
wrappers for the methods, or make the methods public. If a test case can
easily use a method, clients ought to be allowed to use it the same way,
too.
If the method is not yet ready for re-use by production code, the programmer
can supply a sternly-worded comment. Static type checking becomes less
important as programmers invest in test cases for run-time checking.
When programmers add new tests to legacy code, they should start low, to
test soft targets and begin to make the code ready for refactoring and more
tests. Low targets are typically private. I publish them like this:
#ifdef _DEBUG
# define DEBUG_(X_) X_
#else
# define DEBUG_(X_)
#endif
....
private:
DEBUG_(public:)
void secretlyTestMe();
Now a test case can grab secretlyTestMe(), but when we compile in release
mode we still get the static type checking on this class's private things.
(_DEBUG is often defined when the Standard NDEBUG is not, and the DEBUG_()
macro now becomes useful for other optional code, too.)
Some systems can make the test case a friend. I prefer test runners like
CppUnitLine that use a Test Collector to dynamically generate a class for
each test case. This thing's name is difficult to type, and classes should
have more than one test case. Listing them in each tested class is not just
a pain, it's also an invitation for cascading recompiles.
--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!