A bad system design can lead to much hard work. In order to increase the unit tests coverage, I recently started to work on writing unit tests for some classes. One of the case is I want to test a method as follow:
1 2 3 4 5 6 7 8
publicfinal ReturnType getMethod(SomeRequest someRequest){ AnotherRequest anotherRequest = new AnotherRequest(someRequest); SomeResponse someResponse = SomeService.getInstance().someMethod(anotherRequest); SomeValue someValue = someResponse.getValue(); /** * Some processes with someValue.. */ }
The main purpose of this test is testing the process with someValue, so I should just mock the .getValue() method. But the thing is not that easy. Let me put more related classes here: SomeService.class
1 2 3 4 5 6 7 8 9 10 11 12 13
publicfinalclassSomeService{ privatestatic SomeService instance = new SomeService(); static { // A static block } protectedSomeService(){} publicstatic SomeService getInstance(){ return instance; } public SomeResponse someMethod(AnotherRequest anotherRequest){ // Implementation of the method.. } }
SomeResponse.class
1 2 3 4 5
publicclassSomeResponse{ public SomeValue getValue(){ // Implementation of getValue() } }
SomeValue.class
1 2 3 4 5 6 7 8 9
publicclassSomeValue{ private String name; privatevoidpopulateValue(PreDefinedType preDefinedType){ // Generate name from a preDefinedType, basically a black box. } public String getName(){ return name; } }
If I put the unit test as follow, I will get the java.lang.reflect.InvocationTargetException Error.
1 2 3 4 5 6
@RunWith(PowerMockRunner.class) @PrepareForTest({SomeService.class, SomeResponse.class, SomeValue.class, SomeRequest.class}) publicclassElasticSearchBasedTokenGeneratorServiceTestextendsPowerMockTestCase{ // Mock every class and call.. // Find out more on https://github.com/jayway/powermock/wiki/MockitoUsage }
It seems that mock library does not always work. A better way to do it is to create a Utilities.class to reset finalstatic fields.
1 2 3 4 5 6 7 8 9 10
publicclassUtilities{ publicstaticvoidsetFinalStatic(Field field, Object newValue)throws Exception { field.setAccessible(true); // remove final modifier from field Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } }
// Mock response and its related value SomeResponse someResponse = PowerMockito.mock(SomeResponse.class); SomeValue someValue = PowerMockito.mock(SomeValue.class); Mockito.when(someValue.getName()).thenReturn("some name"); Mockito.when(someResponse.getValue()).thenReturn(someValue);
// Mock the service SomeService someService = PowerMockito.mock(SomeService.class); Mockito.when(someService.someMethod(Mockito.any())).thenReturn(someResponse);
// To initialize the service, use the reflect method created above Utilities.setFinalStatic(SomeService.class.getDeclaredField("someService"), someService); SomeService someService = new SomeService(); someService.someMethod(someRequest); // Assertion.. } }
One thing I should mention is, in the actual SomeService.class, we should change the initialization of service outside the method (as a field):
Comments