How do I test local inner class methods in java? [duplicate]
In many application I often have algorithms which make use of dedicated sub-algorithms (or simply well defined pieces of code).
Till now, when I wrote the main algorithm, i created a private method for each sub-algorithm like in the example below (OldStyle):
public class OldStyle {
public int mainAlg() {
int x = subAlg01();
int y = subAlg02();
int z = x * y;
return z;
}
private int subAlg01() {
return 3;
}
private int subAlg02() {
return 5;
}
}
This worked开发者_StackOverflow中文版 fine but I didn't like having a proliferation of methods (subAlg01 and subAlg02) which, even if private, were only used by one method (mainAlg).
Recently I dicovered the use of local inner classes and now my example is (NewStyle):
public class NewStyle {
public int mainAlg() {
class Nested {
public int subAlg01() {
return 3;
}
public int subAlg02() {
return 5;
}
}
Nested n = new Nested();
int x = n.subAlg01();
int y = n.subAlg02();
int z = x * y;
return z;
}
}
I like it very much but now I have the following problem: how do I test subAlg01 and subAlg02 using JUnit?
By the way: I'm using eclipse.
Thanks for you help.
Edit: I try to explain better: I have, let's say, a sorting algorithm and I want to test it to be sure it runs as expected. This sorting algorithms is used by only method m of class X. I could make it a private method of class X but class X usually has nothing to do with sorting, so why "spoil" class X with the sorting method? So I put it inside method m. Some time later I want to improve my sorting algorithm (I make it faster) but I want to be sure that it's behavior is as expected, so I want to re-test it with the original tests.
That's what I want to do, maybe there is no solution, I hope someone may help me.
Edit after answer choice. I selected Rodney answer because his solution is the one I adopted: a standalone helper class helps me (it's a helper!) to have a clear view of what are the sub methods and it also gives me the ability to test them.
You should only test the public interface of classes, not private members or private inner classes. Private members are meant to be implementation details, used only by public methods of the class (directly or indirectly). So you can unit test these indirectly via their caller methods. If you feel you don't have enough granularity in those unit tests, or that you can't sense (some of) the results you are interested in, this probably signs a problem with your design: the class may be too big, trying to do too much, thus some of its functionality may need to be extracted into a separate class, where it can then be unit tested directly.
In the current example, if the inner class itself contains a lot of code, you may simply turn it into a top-level class, then you can unit test its methods directly.
(Btw your inner class should be static
if it doesn't need to refer to the enclosing class instance.)
Filippo, I understand your frustration with the problem and with some of the answers. When I first started using JUnit many years ago, I too wanted to test private code, and I thought it silly of the gurus to say it was a bad idea.
Turns out they were right (surprise!) but only after writing quite a few tests did I understand why. You might need to go through the same process, but you will eventually come to the same conclusion ;-)
Anyway, in your situation, I would make Nested
into a proper standalone class, possibly in a separate package to make obvious that it's a helper classes. Then I would write tests for it directly, independent of any other tests.
Then I'd write tests for NewStyle
and focus only on the behaviour of NewStyle
.
(Quite probably I would also inject Nested
into NewStyle
rather than instantiating it within NewStyle
-- i.e. make it an argument to NewStyle
's constructor.
Then when I write tests for NewStyle
in the test I'd pass in an instance of Nested
and carry on. If I felt particularly tricky, I'd create an interface out of Nested
and create a second implementation, and test NewStyle
with that, too.)
You cannot reach these classes from the outside, so junit cannot test Them. You must have things public to test Them.
You should avoid over complicating your code just because you
don't like having a proliferation of methods
In your case you can test the methods if they are methods of the outer class or if you absolutely need the inner class then place it outside the mainAlg() method so it is visible globally.
public class NewStyle {
public int mainAlg() {
Nested n = new Nested();
int x = n.subAlg01();
int y = n.subAlg02();
int z = x * y;
return z;
}
class Nested {
public int subAlg01() {
return 3;
}
public int subAlg02() {
return 5;
}
}
}
this can then be called using new NewStyle().new Nested().subAlg01();
Hm, I know that languages like Groovy are often used to do unit testing on Java, and are able to access private fields and methods transparently.....but I'm not sure about nested classes. I can understand why you might want to do this sort of thing, but I kind of take the same position I take with testing getters and setters, they should be tested as a side effect of testing your public methods, and if they don't get tested that way, then why are they there to begin with?
The issue comes when your inner class's behavior happens to be a core part of what a method does: Say that you have a method is supposed to pass a runnable to a third object, which happens to be an inner class that really shouldn't be a separate entity: In this case, proper unit testing pretty much requires exercising said inner class by mocking that third object, and using argument capture to test it does what it should.
This kinds of issues are very common in languages that have more support for functional programming, where testing lambdas passed to third parties is pretty much a requirement.
But, as it was said before, if your inner class is never used by an outside party, then it's just an implementation detail, and testing it separately is just not useful.
Actually, you can test your local inner class. But it needs to implement an interface. This way you can create an instance of the local class via reflection and cast it to it's interface. Once you have the interface type, you can test your implementing code without problems:
The interface:
public interface Algorithm {
int subAlg01();
int subAlg02();
}
The class:
public class NewStyle {
public int mainAlg() {
class Nested implements Algorithm {
public int subAlg01() {
return 3;
}
public int subAlg02() {
return 5;
}
}
Nested n = new Nested();
int x = n.subAlg01();
int y = n.subAlg02();
int z = x * y;
return z;
}
}
The test:
public class NewStyleTest {
@Test
public void testLocal() throws ClassNotFoundException,
NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
Class<?> forName = Class.forName("NewStyle$1Nested");
Constructor<?> declaredConstructor = forName
.getDeclaredConstructor(NewStyle.class);
declaredConstructor.setAccessible(true);
Algorithm algorithm = (Algorithm) declaredConstructor
.newInstance(new NewStyle());
assertEquals(algorithm.subAlg01(), 3);
assertEquals(algorithm.subAlg02(), 5);
}
}
精彩评论