Many times I am involved in the design/implementation of APIs I am facing this dilemma.
I am a very strong supporter of information hiding and try to use various techniq
SomethingThatExpectsMyInterface
can be tested outside Foo
, right? You can call its submit()
method with your own test class that implements MyInterface
. So that unit is taken care of. Now you are testing Foo.someMethod()
with that well-tested unit and your untested inner class. That's not ideal - but it's not too bad. As you test-drive someMethod()
, you are implicitly test-driving the inner class. I know that's not pure TDD, by some strict standards, but I would consider it sufficient. You're not writing a line of the inner class except to satisfy a failing test; that there's a single level of indirection between your test and the tested code doesn't constitute a big problem.
In your example it looks like the Foo class really needs a collaborator InnerFoo.
In my opinion the tension between information hiding and testability is solved by the "composite simpler than the sum of its parts" motto.
Foo is a facade over a composite (just InnerFoo in your case, but does not matter.) The facade object should be tested on its intended behaviour. If you feel that the InnerFoo object code is not driven enough by the tests on the behaviour of Foo, you should consider what InnerFoo represents in your design. It may be that you miss a design concept. When you find it, name it, and define its responsibilities, you may test its behaviour separately.
I don't see how information hiding, in the abstract, is reducing your testability.
If you were injecting the SomethingThatExpectsMyInterface
used in this method rather than constructing it directly:
public void someMethod() {
// calculate x, y and z
SomethingThatExpectsMyInterface something = ...;
something.submit(new InnerFoo(x, y, z));
}
Then in a unit test you could inject this class with a mock version of SomethingThatExpectsMyInterface
and easily assert what happens when you call someMethod()
with different inputs - that the mockSomething
receives arguments of certain values.
I think you may have over-simplified this example anyway as InnerFoo cannot be a private class if SomethingThatExpectsMyInterface
receives arguments of its type.
"Information Hiding" doesn't necessarily mean that the objects you pass between your classes need to be a secret - just that you aren't requiring external code using this class to be aware of the details of InnerFoo
or the other details of how this class communicates with others.
what I don't like is making any sacrifices when it comes to information hiding
First, work with Python for a few years.
private
is not particularly helpful. It makes extension of the class hard and it makes testing hard.
Consider rethinking your position on "hiding".
I think you should reconsider using reflection.
It has its own downsides but if it allows you to maintain the security model you want without dummy code, that may be a good thing. Reflection is often not required, but sometimes there is no good substitute.
Another approach to information hiding is to treat the class/object as a black box and not access any non-public methods (Though this can allow tests to pass for the "wrong" reasons i.e. the answer is right but for the wrong reasons.)
My go-to answer for this type of thing is a "test proxy". In your test package, derive a subclass from your system under test, which contains "pass-through" accessors to protected data.
Advantages:
Disadvantages:
final
)In short, this should normally be a rarity. I tend to use it only for UI elements, where best practice (and default behavior of many IDEs) is to declare nested UI controls as inaccessible from outside the class. Definitely a good idea, so you can control how callers get data from the UI, but that also makes it difficult to give the control some known values to test that logic.