- Hum... You can't because they're private dummy.
- And what about using reflection to make them accessible ?
Because we don't have access to private methods, we generally don't test them. This can be a weakness in your testing strategy. Private methods are usually a very sensible part of your code. I've have seen a lot of developers modifying the visibility of their code from private to protected. That's a bad practice. Don't change the visibility of your code for testing purposes.
A solution consists of using reflection to make these private methods accessible for testing.
I really don't like to use reflexion in my application code because if someone is doing some refactoring like renaming a method and forget to update the reflexive code part, you will get a very bad runtime exception. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it.
Test code is not application code. Your tests does not go into production, that's why I'm not afraid of using reflection in my test classes.
Here's an example :
MyClass.java
public class MyClass {
private String myPrivateMethod(Long id) {
//Do something private
return "SomeString_" + id;
}
}
MyClassTest.java
import java.lang.reflect.Method;
import static org.junit.Assert.*;
import org.junit.Test;
public class MyClassTest {
private MyClass underTest;
@Test
public void testMyPrivateMethod() throws Exception {
underTest = new MyClass();
Class[] parameterTypes = new Class[1];
parameterTypes[0] = java.lang.Long.class;
Method m = underTest.getClass().getDeclaredMethod("myPrivateMethod", parameterTypes);
m.setAccessible(true);
Object[] parameters = new Object[1];
parameters[0] = 5569L;
String result = (String) m.invoke(underTest, parameters);
//Do your assertions
assertNotNull(result);
}
}
You should not even be tempted into testing private methods. Private methods should be private because they are considered internal workings of a class.
ReplyDeleteIf you find yourself wanting to test these, it's because you are trying to test how your code works, rather than its actual goal.
have to agree unit testing is not necessarily testing every method, but a unit of work.
ReplyDeleteI think it is a valid approach! Thanks for this!
ReplyDeleteThe programming world is not perfect and so not all programmers are perfect in splitting code into testable units.
Another simpler solution (only for me!) is to make the private methods package protected ...
Well I knew when writing this article that a lot of people will disagree.
ReplyDeleteBut one day I found myself in a weird situation because something went wrong in one of my private method because someone made a little modification to it. I was testing the goal of the public method using it and not the limits of my private method so the tests didn't actually shows anything. And guess what happens some days later, phones ringing and people screaming because our application was down...
Private methods are supporting methods to the exposed API. Using proper code coverage (Cobertura, Clover, Emma, etc), you can verify that you're testing all branches in private methods.
ReplyDeleteIf you cannot test the exposed API and cover all branches in a private method, then you probably need to refactor, or you're not using enough test data. You shouldn't just test the "happy path." There are many edge cases and boundary conditions that need to be tested as well.
I've had people on my team insist on making private methods as protected (or default). There have been many debates on this issue.
A private method call generator: http://code.google.com/p/hexacta-booster/
ReplyDeleteEnjoy!
Challenges to test private methods can also be smell of bad design. By keeping Single Responsibility Principle in mind and by keeping method implementation on single abstraction level one can spot functionality that can be delegated to some other class to be tested separately through public api.
ReplyDeleteGreate example
ReplyDeleteSimple and clear
I tried as in this example and works fine.
ReplyDeleteI then tried a simillar example, instead of a Long parameter replaced with an Array of File objects.
and the line written as
parameterTypes[0] = java.util.Arrays.class;
and now when i try creating the Method object it fails.
could you please tell me what I am doing wrong.
I disagree with those who say it's bad to test private methods. There is no one size fits all rule. Sometimes a private method is complex and worth testing alone.
ReplyDeleteSometimes a very complex object is passed into a public api, but you are only interested in testing a portion of the functionality.
From the extreme-programming point of view (and my own) private methods should be tested. If I'm writing test cases before writing methods themselves, then to write a private method I first need to write a test for it. I'll keep testing my private methods. (But for convenience's sake, I may make some or all of them public.)
ReplyDeleteIn JUnit never forget to extend the TestCase class while writing your tescases. otherwise exceptions will be thrown.
ReplyDeleteThat is the old way Garima. No need to extend the TestClass class with Junit4 and the @Test annotation. Also there's no need to start your your method name with test.
ReplyDeleteI don't like the reflection API for the same reasons you mentioned, but www.dp4j.com should solve all that still using the reflection API; What changes is that you write plain old good direct members access, and dp4j converts that into the reflection calls (roughly 4 lines per line) directly into the AST.
ReplyDeleteI too, but how use dp4j with Eclipse?
DeleteIt doesn't really work with Eclipse, unless you use the official Java Compiler.
Deletehttp://dp4j.com/faq#TOC-Does-it-work-with-Eclipse-
In Java 1.6 and 1.7 you can write:
ReplyDeleteMethod m = underTest.getClass().getDeclaredMethod("myPrivateMethod", long.class);
m.setAccessible(true);
String result = (String) m.invoke(underTest, 5569L);
//Do your assertions
assertNotNull(result);