Invoking a private method via JMockit to test result
As mocking private methods is not allowed in latest Jmockit. One can mock the APIs used inside that private method as a Workaround instead of mocking the private method.
This workaround can also be treated as a final solution.
Example:
Actual Class:
class A {
private int getId(String name){ //private method
return DAOManager.getDao().getId(name); //Call to non-private method can be mocked.
}
}
Test Class:
public class ATest{
@Before
public void setUp(){
new MockDAOManager();
}
//Mock APIs used by the private method `getId`.
public static class MockDAOManager extends MockUp<MockDAOManager>{
static mocked_user_id = 101;
@Mock
public DAOManager getDao() throws Exception{
return new DAOManager();
}
@Mock
public Integer getId(String name){
return mocked_user_id;
}
}
}
Note:
- If you don't have such logic(private method calls to another non-private method) then you may have to refactor your code, Otherwise this will not work.
- Here
DAOManager.getDao().getId(name)
is not a private API. - There may be a need to mock all APIs used by that private method.
At this point, I don't know if JMockit can or should be used for this. Testing my private method can be done with plain old reflection, although I started this exercise to learn about JMockit (and test my code). In case JMockit cannot be used for this, here is how I can use reflection instead.
@Test
public void testParsingForCommas() throws Exception {
StringToTransaction tested = new StringToTransaction();
ArrayList<String> expected = new ArrayList<>();
expected.add("Test");
Method declaredMethod =
tested.getClass().getDeclaredMethod("parseTransactionString",
String.class);
declaredMethod.setAccessible(true);
Object actual = declaredMethod.invoke(tested, "blah blah");
assertEquals(expected, actual);
}
The call to setAccessible(true)
is important here or the invoke
will blow up when calling a private method.
declaredMethod.setAccessible(true);
But you want to know what is really cool? If you don't call setAccessible(true)
, it will blow up with a java.lang.StackOverflowError
! :)
I think you are making this too complicated. You should not be using the Expectations block at all. All you need to do is something like this:
@Test
public void testParsingForCommas() {
StringToTransaction tested = new StringToTransaction();
List<String> expected = new ArrayList<String>();
// Add expected strings list here..
List<String> actual = Deencapsulation.invoke(tested, "parseTransactionString", "blah blah");
assertEquals(expected, actual);
}
Basically, call a private method via Deencapsulation and test that the actual is equal to the expected. Just like you would if the method were public. No mocking is being done, so no Expectations block is needed.