The layout of a private method in the same class that is being tested

I have a Java class called MyClass that I want to test with JUnit. The public methodA method that I want to test calls a private method, methodB , in the same class to determine which conditional path to follow. My goal is to write JUnit tests for different paths in methodA . In addition, methodB calls the service, so I don't want it to run when I run JUnit tests.

What is the best way to mock methodB and control its return so that I can test different paths for methodA?

I prefer to use JMockit when writing mocks, so I am particularly interested in any answer that applies to JMockit.

Here is my sample class:

 public class MyClass { public String methodA(CustomObject object1, CustomObject object2) { if(methodB(object1, object2)) { // Do something. return "Result"; } // Do something different. return "Different Result"; } private boolean methodB(CustomObject custObject1, CustomObject custObject2) { /* For the sake of this example, assume the CustomObject.getSomething() * method makes a service call and therefore is placed in this separate * method so that later an integration test can be written. */ Something thing1 = cobject1.getSomething(); Something thing2 = cobject2.getSomething(); if(thing1 == thing2) { return true; } return false; } } 

This is what I have so far:

 public class MyClassTest { MyClass myClass = new MyClass(); @Test public void test_MyClass_methodA_enters_if_condition() { CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); // How do I mock out methodB here to return true? assertEquals(myClass.methodA(object1, object2), "Result"); } @Test public void test_MyClass_methodA_skips_if_condition() { CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); // How do I mock out methodB here to return false? assertEquals(myClass.methodA(object1, object2), "Different Result"); } } 

Thanks!

+6
source share
5 answers

To give the answer you asked (using JMockit, partial bullying):

 public class MyClassTest { @Tested MyClass myClass; @Test public void test_MyClass_methodA_enters_if_condition() { final CustomObject object1 = new CustomObject("input1"); final CustomObject object2 = new CustomObject("input2"); new NonStrictExpectations(myClass) {{ invoke(myClass, "methodB", object1, object2); result = true; }}; assertEquals("Result", myClass.methodA(object1, object2)); } @Test public void test_MyClass_methodA_skips_if_condition() { final CustomObject object1 = new CustomObject("input1"); final CustomObject object2 = new CustomObject("input2"); new NonStrictExpectations(myClass) {{ invoke(myClass, "methodB", object1, object2); result = false; }}; assertEquals("Different Result", myClass.methodA(object1, object2)); } } 

However, I would not recommend doing so. In general, private methods should not be mocked. Instead, make fun of the actual external dependency of your device under test (in this case, CustomObject ):

 public class MyTestClass { @Tested MyClass myClass; @Mocked CustomObject object1; @Mocked CustomObject object2; @Test public void test_MyClass_methodA_enters_if_condition() { new NonStrictExpectations() {{ Something thing = new Something(); object1.getSomething(); result = thing; object2.getSomething(); result = thing; }}; assertEquals("Result", myClass.methodA(object1, object2)); } @Test public void test_MyClass_methodA_skips_if_condition() { new NonStrictExpectations() {{ object1.getSomething(); result = new Something(); object2.getSomething(); result = new Something(); }}; assertEquals("Different Result", myClass.methodA(object1, object2)); } } 
+2
source

Don't be tempted to mock private methods, even if you can trick yourself into doing this using a mocking tool. Private members are implementation details that you are free to modify. Instead, use a non-private API to implement the class. If this is troublesome, consider moving the nasty code to another class if it is not already there, and use dependency injection to inject the mock implementation of the problem code.

+3
source
 import org.easymock.EasyMock; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({ MyClass.class }) public class MyClassTest { // Class Under Test MyClass cut; @Before public void setUp() { // Create a new instance of the service under test (SUT). cut = new MyClass(); // Common Setup // TODO } @Test public void testMethodA() throws Exception { /* Initialization */ CustomObject object2 = PowerMock.createNiceMock(CustomObject.class); CustomObject object1 = PowerMock.createNiceMock(CustomObject.class); MyClass partialMockCUT = PowerMock.createPartialMock(MyClass.class, "methodB"); long response = 1; /* Mock Setup */ PowerMock .expectPrivate(partialMockCUT, "methodB", EasyMock.isA(CustomObject.class), EasyMock.isA(CustomObject.class)).andReturn(true) .anyTimes(); /* Mock Setup */ /* Activate the Mocks */ PowerMock.replayAll(); /* Test Method */ String result = partialMockCUT.methodA(object1, object2); /* Asserts */ Assert.assertNotNull(result); PowerMock.verifyAll(); } } 
+1
source

Make methodB a member of a separate class and have a private reference to this class inside MyClass .

 public class MyClass { private MyOtherClass otherObject = new MyOtherClass(); public String methodA(CustomObject object1, CustomObject object2) { if(otherObject.methodB(object1, object2)) { // Do something. return "Result"; } // Do something different. return "Different Result"; } } class MyOtherClass { public boolean methodB(CustomObject custObject1, CustomObject custObject2) { // Yada yada code } } 

Personally, I usually check public methods and review coverage reports to make sure all paths have been visited in my personal methods. If I really need to test a private method, it is a smell that requires refactoring, as I already mentioned above.

You can also use reflection, but I would feel dirty. If you REALLY want this solution to let me know, and I will add it to this answer.

0
source

To make fun of a private method, you will need powermock
The sample code will be like this, but I did not run it.

  import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.modules.junit4.PowerMockRunner; @RunWith (PowerMockRunner.class) public class MyClassTest { @Test public void test_MyClass_methodA_enters_if_condition() { final MyClass myClass = Mockito.mock (MyClass.class); CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); Mockito.when (myClass.methodB(object1, object2)).thenReturn (true); Mockito.when (myClass.methodA(object1, object2)).thenCallRealMethod (); assertEquals(myClass.methodA(object1, object2), "Result"); } } 
-1
source

Source: https://habr.com/ru/post/957102/


All Articles