SSLContext 模拟未按预期运行

Vin*_*eet 2 java junit mockito powermock powermockito

我有以下简单的类: import javax.net.ssl.SSLContext;

public class AClass {
    public void someMethod() throws Exception {
        SSLContext context = SSLContext.getInstance("SSL");
        context.init(null, null, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

及其 JUnit: import javax.net.ssl.SSLContext;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SSLContext.class })
public class ATest {
    @Test
    public void testSomeMethod() throws Exception {
        PowerMockito.mockStatic(SSLContext.class);
        SSLContext context = Mockito.mock(SSLContext.class);
        Mockito.when(context.getInstance("SSL")).thenReturn(context);
        new AClass().someMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

JUnit 失败并显示以下堆栈跟踪:

java.lang.NullPointerException
    at javax.net.ssl.SSLContext.init(Unknown Source)
    at random.AClass.someMethod(AClass.java:8)
    at random.ATest.testSomeMethod(ATest.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:316)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:300)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:288)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:208)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:121)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:123)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪指向context.init(null, null, null);具有空指针的行。然而,当我调试(在 Eclipse 中)时,我可以清楚地看到变量的值contextMock for SSLContext, hashCode: 1857173583. 如果是模拟,那么像这样的 void 方法init()不应该执行任何操作。那么,为什么它会抛出一个NullPointerException

Mor*_*fic 5

查看抛出该方法的API和反编译签名,它显示为final,这是基本的Mockito.mock()无法处理的initNPE.

\n

最终方法

\n

另一方面,javadocPowerMockito.mock() reads:

\n
\n

org.powermock.api.mockito.PowerMockito

\n

public static T mock(Class type)
\n创建一个支持模拟最终方法和本机方法的模拟对象。

\n类型参数:
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0T - 模拟对象的类型 \n参数
:
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0type - 模拟对象的类型
\n返回:
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0the mock object.

\n
\n

因此,稍微改变一下你的测试应该可以让它工作:

\n\n
@RunWith(PowerMockRunner.class)\n@PrepareForTest({SSLContext.class})\npublic class ATest {\n    @Test\n    public void testSomeMethod() throws Exception {\n        // create the mock to return by getInstance()\n        SSLContext context = PowerMockito.mock(SSLContext.class);\n\n        // mock the static method getInstance() to return above created mock context\n        PowerMockito.mockStatic(SSLContext.class);\n        Mockito.when(SSLContext.getInstance("SSL")).thenReturn(context);\n\n        // invoke the object under test\n        new AClass().someMethod();\n\n        //TODO - add verifications / assertions\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

更新:

\n

由于您正在使用以下命令运行测试PowerMockRunner, you can also replace

\n
SSLContext context = PowerMockito.mock(SSLContext.class);\n
Run Code Online (Sandbox Code Playgroud)\n

有一个字段

\n
@Mock\nprivate SSLContext context;\n
Run Code Online (Sandbox Code Playgroud)\n

这也将由 PowerMock 处理(或使用MockitoJUnitRunner如果您只需要基本的 mockito,则使用)

\n