SSLContext mock的行为不符合预期

时间:2016-07-11 18:26:28

标签: 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);
    }
}

它的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();
    }
}

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)

堆栈跟踪指向context.init(null, null, null);具有空指针的行。但是当我调试(在Eclipse中)时,我可以清楚地看到context变量的值是Mock for SSLContext, hashCode: 1857173583。如果是mock,那么像init()这样的void方法就不应该做任何事情。那么,为什么要抛出NullPointerException

1 个答案:

答案 0 :(得分:2)

查看引发init的{​​{1}}方法的API和反编译签名,显示为最终,基本为Mockito.mock() can not handle

Final method

另一方面,NPE的javadoc读取:

  

org.powermock.api.mockito.PowerMockito

     

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

  类型参数:
  T - 模拟对象的类型
  参数:
  type - 模拟对象的类型
  返回:
  模拟对象。

所以,改变一下你的测试应该可以使它工作:

PowerMockito.mock()

<强>更新

由于您使用@RunWith(PowerMockRunner.class) @PrepareForTest({SSLContext.class}) public class ATest { @Test public void testSomeMethod() throws Exception { // create the mock to return by getInstance() SSLContext context = PowerMockito.mock(SSLContext.class); // mock the static method getInstance() to return above created mock context PowerMockito.mockStatic(SSLContext.class); Mockito.when(SSLContext.getInstance("SSL")).thenReturn(context); // invoke the object under test new AClass().someMethod(); //TODO - add verifications / assertions } } 运行测试,因此您也可以替换

PowerMockRunner

带字段

SSLContext context = PowerMockito.mock(SSLContext.class);

也将由PowerMock处理(如果您只需要基本的模拟,则使用@Mock private SSLContext context;