无法使用PowerMockito / Mockito模拟URL类

时间:2016-02-19 18:12:53

标签: junit mocking mockito powermock powermockito

我正在尝试使用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()"并改变"然后返回" to" thenAnswer"我可以让它触发。唯一的问题是我从来没有得到我的代码的URL调用。我看到的是对URL.class的3参数构造函数的调用以及参数的所有空值。难道这个类来自Java运行时并且由测试运行器引导?非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

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

  

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

来自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;
    }
}

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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()"并改变"然后返回"至   " thenAnswer"我可以让它触发。唯一的问题是我永远不会得到   我的代码的URL调用。我所看到的是对...的调用   URL.class的3参数构造函数,包含所有空值   参数。

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