I've been using
JavaUnit lately, and have come up with the following technique that I think might make a good idiom.
I have a class (let's call it App
Thingy). I want to test it. So that the tests can get at App
Thingy non-public methods, I first wrote Thingy
Test (a subclass of
TestCase) as an inner class of App
Thingy. I realized, however, that doing so would force everyone who wants to use the App
Thingy class to have JUnit.
Therefore,
I created a subclass of App
Thingy called App
Test
Thingy and moved the inner class there. Now instances of Thingy
Test can poke around in App
Thingy objects, yet App
Thingy itself is "test-free".
--
BillTrost
SubclassToTest is an
AntiPattern. See
SubclassToTestAntiPattern.
See also: SubclassToTest
- Bill, you really had to get "inside" the class? I haven't had to do that so far. -- MichaelFeathers
- I would say that if you're getting inside the class then you're not testing, you're debugging. And if I felt that I needed to leave 'debugging'' logic in place, I would take that as CodeSmells that needed to be sniffed out. -- KielHodges
- I think I was being unclear. I rephrased my text to talk about "non-public" methods. I'm still not clear on Java's scoping rules. I guess I am going to have to figure it out someday. -- BT
- I believe that you'll find that the inner class of the derived class has less access than an inner class of the original class. In particular, don't expect access to private methods and members. -- Kiel
- That depends on the compiler. Some allow the access and some don't, which is even worse. -- DaveHarris
- Ouch! Anybody know what the language spec says about it? -- Kiel
- Yes. Your assertion above is correct; the inner class of the derived class will have less access. Inner classes have access to everything that the outer class has access to. Other classes in the same package (whether subclasses or not) have access to everything except private members, including members with default (or "package") access. Subclasses outside the package have access to public and protected members only. Everything else just gets the public stuff. -- GlennVanderburg, Java language lawyer
I agree with Bill, sometimes you have to get inside the class to make it do things it doesn't normally. If you didn't like subclassing, you'll hate
ThrowYourOwnException! Unless you get inside the class, you are just
TestingByPokingAround. I have adopted Bill's idea as
SubclassToTest. --
JohnFarrell
Unless I'm missing something here, you say that I am just TestingByPokingAround if my automated unit tests access the class being tested using only the API available to any client class. Please enlighten me. -- KielHodges
If your class is deterministic and self-contained, unit tests which call the class and check the answer (
CallAndCheckResult) should be good enough. But if your class depends on external systems which have a lot of state, such as servers across networks, or shared databases, you need to go much further than that. Making sure that the class gives the right answer under strictly controlled conditions is only just scratching the surface in these cases, and that is why I claim it is
TestingByPokingAround. For more confidence, you need techniques like
FakeTheSideEffects and
ThrowYourOwnException. Sorry for my blanket statement, but testing distributed non-deterministic systems is my focus at the moment. --
JohnFarrell
Java anonymous inner classes are great for this. You want a server that blows up the third time some calls it? No problem - right there in the test.
SubclassToTest when you want to override a simple factory method to produce a mock object.
interface BInterface { ... };
class B implements BInterface
{ ... };
class A
{
private BInterface mB;
public A()
{
mB = makeNewB();
};
protected BInterface makeNewB()
{
return new B();
}
....
}
test class...
{
class MockB implements BInterface
{ .... };
class TestA extends A
{
protected BInterface makeNewB()
{
return new MockB();
}
}
... use TestA ....
}
Replacing a method with a testable method by subclassing strikes me as mocking that method, hence
PartialMock which I have tried to automate (
http://www.lastcraft.com/partial_mocks_documentation.php).
Recently I have seen
SubclassToTest a convenient way to test
LegacyCode, typically when you want remove some functionality from a class needed to collaborate with the
ClassUnderTest. --
ErikMeade
I find it preferable to separate my tests into a parallel package structure and build both tests and production code into the same directory. This way my tests have access to all the methods that other classes could have access to (public, protected and default) but I can still hide things from my tests (in private methods). I have always been leery of keeping test code with my production code, especially in the same compilation unit. It becomes very hard (read as harder than necessary) to ship production code without the tests when the tests are part of the production classes (which is the case with Inner Classes). Even subclasses do not have access to private methods so this method is functionally the same as Bill's method at the top of this page but it does not create two classes for each class to test (App
Test
Thingy and Thingy
Test).
See
MockObject for another way doing this kind of testing.