- Mock Private Method
- Foreword
- Mock Private Method
- Refactoring Considerations
- Workaround Using Mockito
- Usage of PowerMock
- Links
- Mock private method
- Mock private method
- Refactoring considerations
- Workaround using Mockito
- Usage of PowerMock
- Links
- 4 thoughts on “ Mock private method ”
- Leave a Reply Cancel reply
- Testing Private method using mockito
Mock Private Method
Join the DZone community and get the full member experience.
Foreword
If you already read some other blog post about unusual mocking, you can skip prelude via this link.
I was asked to put together examples for how to mock Java constructs well known for their testability issues:
I am calling these techniques unusual mocking. I was worried that such examples without any guidance can be widely used by teammates not deeply experienced in mocking frameworks.
Developers practicing TDD or BDD should be aware of testability problems behind these constructs and try to avoid them when designing their tests and modules. That is the reason why you probably wouldn’t be facing such unusual mocking often on project using these great programming methodologies.
But sometimes you have to extend or maintain legacy codebase that usually contains low cohesive classes. In most cases there isn’t time in the current hectic agile world to make such classes easy to unit test the standard way. When you are trying to unit test such classes, you often realize that unusual mocking is needed.
That is why I decided to create and share refactoring considerations alongside with examples and workarounds for unusual mocking. Examples are using Mockito and PowerMock mocking frameworks and TestNG unit testing framework.
Mock Private Method
Refactoring Considerations
Private method that is needed to be mocked can be in:
- testing class (will call it TC)
- direct dependency of testing class (will call is DDC)
- class that is not direct dependency of testing module (will call it NDDC)
Re-factoring techniques to consider:
- If the private method is in TC, it is a good sign that TC has low cohesion (has too many responsibilities) and logic behind private method should be extracted into separate class. After this refactoring, private method in TC becomes public in new dependency class. Than it is possible to mock it standard way.
- If the private method is in DDC, concerns of TC and DDC modules are not separated properly. It means you are trying to test some logic from DDC in test for TC. Consider moving this logic to TC or to separate module. Private method than becomes public and can be mocked standard way.
- If the private method is in NDDC, you are probably creating integration test instead of unit test. Unit test in theory should be testing module in isolation. That means to mock all direct dependencies (sometimes it’s easier to test with real objects when they are simple and independent enough). Consider:
- Creation of unit test first. You can decide later if integration test is needed for group of modules you trying to test.
- Find easier to mock boundaries for your integration test (you can find a clue in unit test for NDDC if exists)
- Refactor NDDC according refactoring practises mentioned for TC and DDC above, update it’s unit test (also all tests affected by refactoring), and use created public method as boundary for your integration test.
Workaround Using Mockito
This is my preferred technique when I need to mock private method. I believe that minor exposing of internal implementation in flavor to enhance testability of testing module is much lower risk for project than fall into bytecode manipulation mocking framework like PowerMock or JMockIt.
- Changing private access modifier to default
- Partially mock testing object by using spy
- Mocking of changed default method with return value
- Mocking of changed changed default void method
- Verifying of changed default method calls
public class Train < public int compose() < for (int idx = 0; idx < getWagonsCount(); idx++) < addWagon(0); >return getWagonsCount(); > /** * Access modifier was changed from private to default to enhance * testability */ // private int getWagonsCount() < throw new UnsupportedOperationException("Fail if not mocked!"); >/** * Access modifier was changed from private to default to enhance * testability */ // private void addWagon(int position) < throw new UnsupportedOperationException( "Fail if not mocked! [position=" + position + "]"); >>
@Test public void testCompose() < Train train = new Train(); Train trainSpy = Mockito.spy(train); //notice different Mockito syntax for spy Mockito.doReturn(TESTING_WAGON_COUNT).when(trainSpy).getWagonsCount(); Mockito.doNothing().when(trainSpy).addWagon(0); // invoke testing method int actualWagonCount = trainSpy.compose(); Assert.assertEquals(actualWagonCount, TESTING_WAGON_COUNT); Mockito.verify(trainSpy, Mockito.times(TESTING_WAGON_COUNT)) .addWagon(0); >
Usage of PowerMock
Before usage of this example, please carefully consider if it is worth to bring bytecode manipulation risks into your project. They are gathered in this blog post. In my opinion it should be used only in very rare and non-avoidable cases.
Test shows how to mock private method directly by PowerMock. Example covers:
- Mocking of private method with return value
- Mocking of private void method
- Verifying of private method calls
public class Truck < public double addLoad(CollectionboxWeightsToAdd) < for (Double boxWeight : boxWeightsToAdd) < addBoxToLoad(boxWeight); >return getLoadWeight(); > private double getLoadWeight() < throw new UnsupportedOperationException("Fail is not mocked!"); >private void addBoxToLoad(double weight) < throw new UnsupportedOperationException("Fail is not mocked! [weight=" + weight + "]"); >>
@PrepareForTest(Truck.class) public class TruckTest extends PowerMockTestCase < private static final double TESTING_LOAD_WEIGHT = 200; private static final double TESTING_BOX_WEIGHT = 100; @Test public void testTestingMethod() throws Exception < Truck truck = new Truck(); Truck truckSpy = PowerMockito.spy(truck); PowerMockito.doReturn(TESTING_LOAD_WEIGHT).when(truckSpy, "getLoadWeight"); PowerMockito.doNothing().when(truckSpy, "addBoxToLoad", TESTING_BOX_WEIGHT); // call testing method CollectionboxesWeights = Arrays.asList(TESTING_BOX_WEIGHT, TESTING_BOX_WEIGHT); double actualLoad = truckSpy.addLoad(boxesWeights); Assert.assertEquals(actualLoad, TESTING_LOAD_WEIGHT); PowerMockito.verifyPrivate(truckSpy, Mockito.times(2)).invoke( "addBoxToLoad", TESTING_BOX_WEIGHT); > >
Links
Other unusual mocking examples:
Mock private method
If you already read some other blog post about unusual mocking, you can skip prelude via this link.
I was asked to put together examples how to mock Java constructs well know for their testability issues:
I am calling these techniques unusual mocking. I was worried that such examples without any guidance can be widely used by teammates not deeply experienced in mocking frameworks.
Developers practicing TDD or BDD should be aware of testability problems behind these constructs and try to avoid them when designing their tests and modules. That is the reason why you probably wouldn’t be facing such unusual mocking often on project using these great programming methodologies.
But sometimes you have to extend or maintain legacy codebase that usually contains low cohesive classes. In most cases there isn’t time in current hectic agile world to make such class easy to unit test standard way. When you are trying to unit test such class you often realize that unusual mocking is needed.
That is why I decided to create and share refactoring considerations alongside with examples and workarounds for unusual mocking. Examples are using Mockito and PowerMock mocking frameworks and TestNG unit testing framework.
Mock private method
Refactoring considerations
Private method that is needed to be mocked can be in:
- testing class (will call it TC)
- direct dependency of testing class (will call is DDC)
- class that is not direct dependency of testing module (will call it NDDC)
Re-factoring techniques to consider:
- If the private method is in TC, it is a good sign that TC has low cohesion (has too many responsibilities) and logic behind private method should be extracted into separate class. After this refactoring, private method in TC becomes public in new dependency class. Than it is possible to mock it standard way.
- If the private method is in DDC, concerns of TC and DDC modules are not separated properly. It means you are trying to test some logic from DDC in test for TC. Consider moving this logic to TC or to separate module. Private method than becomes public and can be mocked standard way.
- If the private method is in NDDC, you are probably creating integration test instead of unit test. Unit test in theory should be testing module in isolation. That means to mock all direct dependencies (sometimes it’s easier to test with real objects when they are simple and independent enough). Consider:
- Creation of unit test first. You can decide later if integration test is needed for group of modules you trying to test.
- Find easier to mock boundaries for your integration test (you can find a clue in unit test for NDDC if exists)
- Refactor NDDC according refactoring practises mentioned for TC and DDC above, update it’s unit test (also all tests affected by refactoring), and use created public method as boundary for your integration test.
Workaround using Mockito
This is my preferred technique when I need to mock private method. I believe that minor exposing of internal implementation in flavor to enhance testability of testing module is much lower risk for project than fall into bytecode manipulation mocking framework like PowerMock or JMockIt.
- Changing private access modifier to default
- Partially mock testing object by using spy
- Mocking of changed default method with return value
- Mocking of changed changed default void method
- Verifying of changed default method calls
public class Train < public int compose() < for (int idx = 0; idx < getWagonsCount(); idx++) < addWagon(0); >return getWagonsCount(); > /** * Access modifier was changed from private to default to enhance * testability */ // private int getWagonsCount() < throw new UnsupportedOperationException("Fail if not mocked!"); >/** * Access modifier was changed from private to default to enhance * testability */ // private void addWagon(int position) < throw new UnsupportedOperationException( "Fail if not mocked! [position=" + position + "]"); >>
@Test public void testCompose() < Train train = new Train(); Train trainSpy = Mockito.spy(train); //notice different Mockito syntax for spy Mockito.doReturn(TESTING_WAGON_COUNT).when(trainSpy).getWagonsCount(); Mockito.doNothing().when(trainSpy).addWagon(0); // invoke testing method int actualWagonCount = trainSpy.compose(); Assert.assertEquals(actualWagonCount, TESTING_WAGON_COUNT); Mockito.verify(trainSpy, Mockito.times(TESTING_WAGON_COUNT)) .addWagon(0); >
Usage of PowerMock
Before usage of this example, please carefully consider if it is worth to bring bytecode manipulation risks into your project. They are gathered in this blog post. In my opinion it should be used only in very rare and non-avoidable cases.
Test shows how to mock private method directly by PowerMock. Example covers:
- Mocking of private method with return value
- Mocking of private void method
- Verifying of private method calls
public class Truck < public double addLoad(CollectionboxWeightsToAdd) < for (Double boxWeight : boxWeightsToAdd) < addBoxToLoad(boxWeight); >return getLoadWeight(); > private double getLoadWeight() < throw new UnsupportedOperationException("Fail is not mocked!"); >private void addBoxToLoad(double weight) < throw new UnsupportedOperationException("Fail is not mocked! [weight=" + weight + "]"); >>
@PrepareForTest(Truck.class) public class TruckTest extends PowerMockTestCase < private static final double TESTING_LOAD_WEIGHT = 200; private static final double TESTING_BOX_WEIGHT = 100; @Test public void testTestingMethod() throws Exception < Truck truck = new Truck(); Truck truckSpy = PowerMockito.spy(truck); PowerMockito.doReturn(TESTING_LOAD_WEIGHT).when(truckSpy, "getLoadWeight"); PowerMockito.doNothing().when(truckSpy, "addBoxToLoad", TESTING_BOX_WEIGHT); // call testing method CollectionboxesWeights = Arrays.asList(TESTING_BOX_WEIGHT, TESTING_BOX_WEIGHT); double actualLoad = truckSpy.addLoad(boxesWeights); Assert.assertEquals(actualLoad, TESTING_LOAD_WEIGHT); PowerMockito.verifyPrivate(truckSpy, Mockito.times(2)).invoke( "addBoxToLoad", TESTING_BOX_WEIGHT); > >
Links
Other unusual mocking examples:
4 thoughts on “ Mock private method ”
Unless I’m mistaken, the Mockito example’s are not private. The access level modifier is omitted, so it is ‘protected’ by default. So if the test is defined in the same package (in this case, net.lkrnac.unusualmockingexamples.privatemethod.mockito), the method is visible to Mockito.spy.
Yes, that is correct. Changing access modifier from private to default is workaround I mentioned in blog post.
You can use java reflection to access private fields in test classes. ex:
Field status= ReflectionUtils.findField(MyClass.class, “status”);
status.setAccessible(true);
ReflectionUtils.setField(status, this.myClass, true);Leave a Reply Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Testing Private method using mockito
It is generally considered bad practice to test private methods, as they are an implementation detail that should not be exposed to the outside world. Private methods are often not designed to be called directly, and they can change without notice, which can break the tests that depend on them.
Instead of testing private methods, it is usually better to test the public methods and the overall behavior of the class. This ensures that the class is correct from the perspective of its users and reduces the risk of breaking the tests when the implementation changes.
If you still want to test a private method, one way to do it is to use reflection to call the method from your test. Here’s an example of how you can use reflection to test a private method using Mockito:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import java.lang.reflect.Method; import static org.junit.Assert.assertEquals; @RunWith(MockitoJUnitRunner.class) public class PrivateMethodTest < @Spy private MyClass myClass; @InjectMocks private MyClassService myClassService; @Test public void testPrivateMethod() throws Exception < Method privateMethod = MyClass.class.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); String result = (String) privateMethod.invoke(myClass); assertEquals("private method", result); > > class MyClass < private String privateMethod() < return "private method"; > > class MyClassService < private final MyClass myClass; MyClassService(MyClass myClass) < this.myClass = myClass; > >
This code defines a test class PrivateMethodTest with a spy of the MyClass class and an instance of the MyClassService class that depends on it. It also defines a test method testPrivateMethod that uses reflection to call the privateMethod method of MyClass .
To use reflection to call the private method, the code first gets a Method object for the privateMethod method using the getDeclaredMethod method of the Class object. It then sets the accessible flag of the Method object to true using the setAccessible method. Finally, it invokes the privateMethod method using the invoke method of the Method object and passes it the instance of MyClass that it wants to call the method on.
I hope this helps! Let me know if you have any questions.