无法使用 PowerMockito/Mockito 模拟 URL 类



我正在尝试使用PowerMockito来模拟我正在测试的代码中创建java.net.URL类。 基本上,我想阻止真正的HTTP请求发生,而是1)在发出请求时检查数据,2)在模拟响应上提供我自己的测试数据。 这就是我正在尝试的:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ URL.class, MockedHttpConnection.class })
public class Test {
    URL mockedURL = PowerMockito.mock(URL.class);
    MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
...
    PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
...
}

我要测试的代码如下所示:

URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();

在我的测试场景中,我模拟了wlInvokeUrlString以匹配"MyURLString"。 我还尝试使用各种其他形式的 whenNew 行,尝试注入模拟。 无论我尝试什么,它都不会拦截构造函数。 我想做的只是"捕获"对 openConnection() 的调用,并让它返回我模拟的 HTTP 连接而不是真正的连接。

我已经在同一脚本中模拟了这个类之前的其他类,这些类按预期工作。 要么我需要第二双眼睛(可能是真的),要么 URL 类有一些独特的东西。 我确实注意到,如果我使用"whenNew(URL.class).withAnyArguments()"并将"thenReturn"更改为"thenAnswer",我可以触发它。 唯一的问题是我从来没有收到我的代码的 URL 调用。 我看到的是 URL 的 3 参数构造函数的调用.class参数的所有空值。 可能是这个类来自 Java 运行时并且由测试运行器引导吗? 任何帮助都非常感谢。

这是使用 PowerMockito.whenNew 时的常见错误。

请注意,您必须准备创建 MyClass 新实例的类进行测试,而不是 MyClass 本身。 例如,如果执行新MyClass()的类称为X,那么您必须执行@PrepareForTest(X.class)才能使何时New 工作

来自Powermock wiki

因此,您需要稍微更改一下测试并添加到@PrepareForTest创建一个类,该类创建URL的新实例,例如:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class})
public class URLTest {
public class URLTest {
    private ConnectionUser connectionUser;
    @Before
    public void setUp() throws Exception {
        connectionUser = new ConnectionUser();
    }
    @Test
    public void testName() throws Exception {
        URL mockedURL = PowerMockito.mock(URL.class);
        MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
        PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
        PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
        connectionUser.open();
        assertEquals(mockedConnection, connectionUser.getConnection());

    }
}

哪里:

public class ConnectionUser {
    private String wlInvokeUrlString = "MyURLString";
    private HttpURLConnection connection;
    public void open() throws IOException {
        URL wlInvokeUrl = new URL(wlInvokeUrlString);
        connection = (HttpURLConnection) wlInvokeUrl.openConnection();
    }
    public HttpURLConnection getConnection() {
        return connection;
    }
}

我不确定.withParameterTypes(x).withArguments(x)之间的区别,但我相信您需要对其进行如下设置才能使代码正常工作。试一试,如果这没有帮助,请告诉我。

PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL);

问题是真实调用的参数与模拟中的预期不匹配。

PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL)将返回mockedURL只有呼叫new URL("MyURLString")

如果将其更改为:

PowerMockito.whenNew( URL.class ).withParameterTypes( String.class )
    .withArguments( org.mockito.Matchers.any( String.class ) ).thenReturn( mockedURL );

它将捕获传递给构造函数的任何字符串URL(String)(甚至null)并返回您的模拟。


当您尝试时

"whenNew(URL.class).withAnyArguments()"并将"thenReturn"更改为 "那么答案"我可以让它触发。唯一的问题是我永远不会得到 我的代码的 URL 调用。我看到的是 URL 的 3 参数构造函数.class所有空值为 参数。

PowerMock 将尝试模拟所有构造函数(org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod在第 122 行),然后调用第一个构造函数(有 3 个参数)并模拟其答案。但是后续调用将返回已经模拟的调用,因为您告诉它模拟任何参数。这就是为什么您在Answer中只看到一个带有null, null, null的呼叫。

最新更新