Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking a private method via JMockit to test result

Am using JMockit 1.1 and all I want to do is invoke a private method and test the return value. However, I am having trouble understanding exactly how to do this from the JMockit De-Encapsulation example.

The method I am trying to test is the private method in this class:

public class StringToTransaction {
   private List<String> parseTransactionString(final String input) {
      // .. processing
      return resultList;
   }
}

And my test code is below.

@Test
public void testParsingForCommas() {
   final StringToTransaction tested = new StringToTransaction();
   final List<String> expected = new ArrayList<String>();
   // Add expected strings list here..
   new Expectations() {
      {
         invoke(tested, "parseTransactionString", "blah blah");
         returns(expected);
      }
   };
}

And the error I am getting is:

java.lang.IllegalStateException: Missing invocation to mocked type at this point; please make sure such invocations appear only after the declaration of a suitable mock field or parameter

Perhaps I have misunderstood the whole API here, because I don't think I want to mock the class.. just test the result of calling the private method.

like image 690
Robert Mark Bram Avatar asked Mar 24 '13 06:03

Robert Mark Bram


People also ask

Is there any direct way to test private method?

The best way to test a private method is via another public method. If this cannot be done, then one of the following conditions is true: The private method is dead code. There is a design smell near the class that you are testing.

Can we test private methods in unit testing JUnit?

The answer is: you don't. You don't verify that one method in a class called another method in that class. And while you can test a private method by changing it to public, you shouldn't.

What is JMockit in Java?

Introduction. First of all, let's talk about what JMockit is: a Java framework for mocking objects in tests (you can use it for both JUnit and TestNG ones). It uses Java's instrumentation APIs to modify the classes' bytecode during runtime in order to dynamically alter their behavior.


2 Answers

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.

like image 90
Jeff Olson Avatar answered Sep 30 '22 00:09

Jeff Olson


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! :)

like image 25
Robert Mark Bram Avatar answered Sep 30 '22 00:09

Robert Mark Bram