I want to unit test Java code that calls System.exit(-1) and want it to just do nothing instead of exiting the process. The underlying reason is that otherwise JaCoCo does not work properly and project guidelines want to see that line covered. Changing the tested code is not an option, too. Other calls to System should work normally. PowerMockito 2.0.7 is already used in the project and should be used here, too. My current Java version is 1.8.0_181 on Windows.
I tried with
PowerMockito.spy(System.class);
PowerMockito.doNothing().when(System.class, "exit", ArgumentMatchers.any(int.class));
//here comes the code under test that calls System.exit
It does not seem to work, System.exit seems to exit the process anyway.
How do it get this to work?
I think you should replace both the lines in your sample code
PowerMockito.spy(System.class);
PowerMockito.doNothing.....
to
PowerMockito.mockStatic(System.class);
This change works in my local as System.exit does nothing because of the mock on static method.
Also, I hope you are using PrepareForTest annotation
#PrepareForTest(CLASS_UNDER_TEST)
The spy method is to call real methods and have some wrapper around the non-static methods. Since you need a mock for static methods, mockStatic method should be used instead.
Update 1
The PowerMockito mockStatic method by default creates mock for all the static methods within the class. I don't have any clean solution. But, I can suggest a solution which looks ugly but does what is needed i.e only mock specific static method and remaining methods are invoking real methods. PoweMockito's mockStatic method is internally calling DefaultMockCreator to mock the static methods.
#RunWith(PowerMockRunner.class)
public class StaticTest {
#Test
public void testMethod() throws Exception {
// Get static methods for which mock is needed
Method exitMethod = System.class.getMethod("exit", int.class);
Method[] methodsToMock = new Method[] {exitMethod};
// Create mock for only those static methods
DefaultMockCreator.mock(System.class, true, false, null, null, methodsToMock);
System.exit(-1); // This will be mocked
System.out.println(System.currentTimeMillis()); // This will call up real methods
}
}
As per the PowerMockito documentation, the right way to call static void method is -
PowerMockito.mockStatic(SomeClass.class);
PowerMockito.doNothing().when(SomeClass.class);
SomeClass.someVoidMethod();
Reference - https://github.com/powermock/powermock/wiki/Mockito#how-to-stub-void-static-method-to-throw-exception
This should create the mock behaviour for the specific static void method. Unfortunately, this doesn't work for System Class because System class is final. Had it been not final, this would have worked. I tried it and I got this exception -
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.System
Mockito cannot mock/spy because :
- final class
Code -
#Test
public void testMethod() throws Exception {
PowerMockito.mockStatic(System.class);
PowerMockito.doNothing().when(System.class);
System.exit(-1); // mockito error coming here
System.exit(-1);
System.currentTimeMillis();
}
Related
I am trying to verify that a static method is called during the test. However, it throws an exception due to the fact that more than one static method of the same class is called during the run. Both these static methods are mocked. The exact exception is like this :-
An unexpected error occurred while verifying a static stub.
To correctly verify a stub, invoke a single static method of
com.booking.capacityservicejobs.models.YourStaticClass in the provided
lambda.
For example, if a method 'sample' was defined, provide a lambda or
anonymous class containing the code.
The code is something like this:-
try (MockedStatic<MyClass1> theMock = Mockito.mockStatic(MyClass1.class);
MockedStatic<MyClass2> configMock = Mockito.mockStatic(MyClass2.class);
MockedStatic<MyClass3> downtimeMock = Mockito.mockStatic(MyClass3.class)) {
theMock.when(MyClass1::lockAndFetch).thenReturn(duRuns);
theMock.when(() -> MyClass1.startAndUnlock(1)).thenAnswer(invocation -> null);
configMock.when(() -> MyClass2.getById(1)).thenReturn(dummyConfig);
downtimeMock.when(() -> MyClass3.isScheduledForRole("app-dummy")).thenReturn(false);
new RunExecutor().run(); //this executes the code to be tested
theMock.verify(() -> MyClass1.startAndUnlock(1));
}
Can I get around this limitation without using powermock? What is the correct way to verify multiple(different) static method calls?
Even if I agree is not a good practice, I have had to deal myself with this issue because I had a dependency with a static utilities class in a library that I could not refactor it, so here goes my answer.
My case it's not exactly as yours: I was testing a method HttpUtils.logProtocolMessages that inside made a call to 2 different static methods in LoggingApi utility class; both mehods returned void. This worked for me:
#Test
public void logProtocolMessageWithoutRequestTest() {
try (MockedStatic<LoggingApi> mockLog = Mockito.mockLog(LoggingApi.class)){
mockLog.when(() -> {
LoggingApi.setProtocolSendMessage(anyString(), anyString());
LoggingApi.setProtocolReceiveMessage(anyString(), anyString(), anyString());
}).thenAnswer(Answers.RETURNS_DEFAULTS);
HttpUtils.logProtocolMessages(mockTarget, HttpClientVersion.HTTP_2, response, null);
mockLog .verify(() -> JcatLoggingApi.setProtocolSendMessage(anyString(), anyString()));
mockLog .verify(() -> LoggingApi.setProtocolReceiveMessage(anyString(), anyString(), anyString()));
}
}
As you can see, the 2 static methods are mocked in the same lambda, both of them instructed to return nothing. I would say that any case going beyond this should consider a redesign (if possible), or even the test itself.
This WILL ONLY work for basic verifications with verify, such as the # of times the mocked methods are invoked. If you try to do more complex checks such as capturing arguments, the limitation of invoking a single static method in the lambda will prevail, and any assertion based on captors will fail because Mockito has not been able to properly inject their values when more than one method in the same mocked static class is invoked.
This is the mockito dependency I used:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
I'm new to using Powermockito to mock static methods. My first attempt is to mock calls on the Environment class. I have a sample test that exemplifies my problem as shown in my test class below. The test is failing, so I'm seeking some help.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Environment.class})
public class StorageUtilsTest {
#Test
#Config(sdk = 22)
public void myTest() {
PowerMockito.mockStatic(Environment.class);
File mockStorageDirectoryFile = Mockito.mock(File.class);
PowerMockito.when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
PowerMockito.when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)).thenReturn(mockStorageDirectoryFile)`;
}
}
I'm trying to mock the static method call to Environment.getExternalStoragePublicDirectory and it returns null giving an exception org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class. The static method call is returning null. I understand that the method call is deprecated in sdk=29, so I've configured the test to run with sdk=22, and it still didn't work. So in an attempt to understand what was going on, I added a call to the getExternalStorageState method as shown above. I get a null return value on this method too. My understanding is that this method should return something, but it also returns null. I'm configuring the test for sdk=22 since I'm setting a targetSdkVersion 29 in my build.gradle.
I'm really confused as to why I can't mock the static methods on the Environment class. Will someone please help me understand what I'm doing wrong?
private void mockStorageServiceFactory(StorageGroupService storageGroupService)
throws StorageServiceException {
PowerMockito.mockStatic(StorageServiceFactory.class);
PowerMockito.when(StorageServiceFactory.getContainerNameForPartner(anyLong()))
.thenReturn("dummyName");
}
Actual method is getting called at 3rd line (when().thenReturn()).
I have already added #RunWith(PowerMockRunner.class) and #PrepareForTest annotations.
Why actual method is called? I tried PowerMockito.doReturn also, but getting same issue.
Sorry, I was running the test case from main method. When i ran it as JUNIT, it is working.
I am using PowerMockito to mock a static method to get a better line coverage. The test passes but Cobertura still shows the return line as red. I am not sure if I am using PowerMockito in a wrong way. Any clues?
MUT
public static Object getBean( String beanName ) {
return AppContext.getApplicationContext().getBean( beanName );
}
Junit test
#RunWith ( PowerMockRunner.class )
#PrepareForTest ( {AppContext.class} )
private ApplicationContext applicationContext;
#Test
public void testGetBean() throws Exception {
String beanName = "junitBean";
applicationContext = Mockito.mock(ApplicationContext.class);
PowerMockito.mockStatic(AppContext.class);
AppContext.setApplicationContext(applicationContext);
PowerMockito.when(AppContext.getApplicationContext()).thenReturn(applicationContext);
PowerMockito.when(AppContext.getApplicationContext().getBean(beanName)).thenReturn(Object.class);
AppContext.getBean(beanName);
}
To mock static methods, PowerMock has to modify the Java Byte Code of your class after it has been compiled. Code coverage tools work in the same way - they "instrument" (google for "bytecode instrumentation") the bytecode in order to collect information about which lines of code have been run.
Given that there are two things here modifying already compiled code according to their own needs, it's not surprising that they don't work well together. In fact, other coverage tools have the same issue e.g. this issue on the PowerMock GitHub page.
It is precisely reasons like this that mocking static methods is a bad idea and should only really be done in very exceptional circumstances. In most cases, you can either wrap the static method call in an interface and pass an instance of the interface to your object under test, or better yet (if the code is under your control) remove the static method and replace it with an instance method.
You could also just pass the ApplicationContext instance directly into the constructor of your class under test.
I'm trying to mock the SecurityManager class. When I run the following code, Mockito throws an exception:
#After
public void tearDown()
{
SecurityManager securityManagerMock = mock(SecurityManager.class);
System.setSecurityManager(securityManagerMock);
}
The stack trace is the following lines repeated indefinitely:
at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:42)
at $java.lang.SecurityManager$$EnhancerByMockitoWithCGLIB$$3ceafc0f.checkMemberAccess(<generated>)
at java.lang.Class.checkMemberAccess(Class.java:2157)
at java.lang.Class.getDeclaredField(Class.java:1879)
at org.mockito.internal.creation.cglib.CGLIBHacker.reflectOnCreateInfo(CGLIBHacker.java:44)
at org.mockito.internal.creation.cglib.CGLIBHacker.setMockitoNamingPolicy(CGLIBHacker.java:20)
at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:42)
at $java.lang.SecurityManager$$EnhancerByMockitoWithCGLIB$$3ceafc0f.checkMemberAccess(<generated>)
at java.lang.Class.checkMemberAccess(Class.java:2157)
at java.lang.Class.getDeclaredField(Class.java:1879)
at org.mockito.internal.creation.cglib.CGLIBHacker.reflectOnCreateInfo(CGLIBHacker.java:44)
at org.mockito.internal.creation.cglib.CGLIBHacker.setMockitoNamingPolicy(CGLIBHacker.java:20)
at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:42)
at $java.lang.SecurityManager$$EnhancerByMockitoWithCGLIB$$3ceafc0f.checkMemberAccess(<generated>)
at java.lang.Class.checkMemberAccess(Class.java:2157)
at java.lang.Class.getDeclaredField(Class.java:1879)
at org.mockito.internal.creation.cglib.CGLIBHacker.reflectOnCreateInfo(CGLIBHacker.java:44)
at org.mockito.internal.creation.cglib.CGLIBHacker.setMockitoNamingPolicy(CGLIBHacker.java:20)
What am I doing wrong here?
PS! You could also mock static method call to getSecurityManager() method.
Mocking Static Method
See maunal at http://code.google.com/p/powermock/wiki/MockitoUsage
Add #PrepareForTest at class level.
#PrepareForTest(System.class); // System.class contains static methods
Call PowerMockito.mockStatic() to mock a static class (use PowerMockito.mockStaticPartial(class, method) to mock a specific method):
PowerMockito.mockStatic(System.class);
Just use Mockito.when() to setup your expectation:
Mockito.when(System.getSecurityManager()).thenReturn(securityManagerMock);
When you change the SecurityManager, you should reset it to the original SecurityManager after the test.
You may use the System Rules library for your test. Setting and resetting the security manager are just two lines of code with this rule.
#Rule
public ProvideSecurityManager provideSecurityManager
= new ProvideSecurityManager(yourSecurityManager);
Within your test yourSecurityManager is used and outside of the test the original security manager is used.