Let's say you have set up a test that constructs a new Alpha class. At the end of this test, you want to assert that bravo.getCharlie() is equal to some expected String. How should the test get access to this String?
One of the interesting ideas behind "test driven design": if your test is hard to write, that's a hint that there may be a flaw in your design.
Alpha constructs a new Bravo class during its own constructor and exposes no public visibility to it.
This is a code smell, a hint that something has gone wrong in your code.
The pattern that your code is matching is that you have a side effect (a write to the state Charlie) that you are trying to evaluate in your test. It's actually pretty common to have side effects that can't be read later (for example, a write to the console).
The common pattern for testing such a thing is to use a TestDouble, so that you can capture the behavior.
But for that to work, you need to implement your code in such a way that it is open to extension.
Misko Hevery wrote a number of essays about issues related to constructors; his Top 10 Things Which Make Your Code Hard to tTest would be a good starting point.
Just ask for all of the collaborators you need in your constructor.
The idea is that while this example is closed to extension
Alpha () {
this.beta = new Beta();
}
making the class open to extension is simply a matter of re-arranging the code slightly.
Alpha (Beta beta) {
this.beta = beta;
}
However, if Alpha()
is part of the published interface, then changing the signature in this way may break backward compatibility. That is usually a bad idea, so you might instead make this change
Alpha () {
this(new Beta());
}
Alpha (Beta beta) {
this.beta = beta;
}
The constructor that is hard to test delegates its work to the constructor that is easy to test.
Of course, this in turn means that the no argument constructor isn't being covered by this test - we've written better code, but our test coverage numbers have gotten "worse". So you have to decide in your mind how to reconcile that.
In my case, I've allowed my self to be comfortable with the recommendations of Kent Beck
I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence....
and C.A.R. Hoare
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies....
The default constructor here meets my bar for "obviously no deficiencies"; the code is too simple to be the source of a fault.