【问题标题】:PowerMock does not mock correctlyPowerMock 无法正确模拟
【发布时间】:2016-08-06 14:23:20
【问题描述】:

我写了一个简单的方法,它应该接受一个 url 并通过一个 get 请求从这个 url 检索数据。该方法如下所示:

public String getResponse(String connectionUrl) throws HttpException {

    HttpURLConnection connection = null;

    try {
        URL url = new URL(connectionUrl);
        connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");

        int responseCode = connection.getResponseCode();
        if (responseCode != 200) {
            throw new HttpException("Response code was " + responseCode + " (should be 200)");
        }

        Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\\A");
        String response = scanner.hasNext() ? scanner.next() : "";

        connection.disconnect();
        scanner.close();

        return response;

    } catch (IOException e) {

        if (connection != null) {
            connection.disconnect();
        }

        throw new HttpException(e.getMessage(), e.getCause());
    }
}

现在我正在为此方法编写单元测试。我使用 PowerMock 和 Mockito 和 JUnit 来编写我的测试。有问题的测试如下所示:

@Test
public void getResponse_NormalResponse() throws Exception {

    String expectedResponse = "This is the expected response text!";
    URL url = PowerMockito.mock(URL.class);
    HttpURLConnection connection = PowerMockito.mock(HttpURLConnection.class);
    InputStream inputStream = PowerMockito.mock(InputStream.class);
    Scanner scanner = PowerMockito.mock(Scanner.class);

    PowerMockito.whenNew(URL.class).withArguments(REQUEST_URL).thenReturn(url);
    PowerMockito.whenNew(Scanner.class).withArguments(inputStream).thenReturn(scanner);
    PowerMockito.when(url.openConnection()).thenReturn(connection);

    // Response code mocked here
    PowerMockito.when(connection.getResponseCode()).thenReturn(200); 

    PowerMockito.when(connection.getInputStream()).thenReturn(inputStream);
    PowerMockito.when(scanner.hasNext()).thenReturn(true);
    PowerMockito.when(scanner.next()).thenReturn(expectedResponse);

    HttpClient httpClient = new HttpClient();
    String actualResponse = httpClient.getResponse(REQUEST_URL);

    Assert.assertEquals("Response is wrong!", expectedResponse, actualResponse);
}

但是当我运行测试时,会抛出 HttpException,因为响应代码是 404。测试是使用 PowerMockRunner 运行的。我做错了什么,为什么这个测试不能正常工作?

【问题讨论】:

  • 我建议重构方法代码,这样您就可以在没有 PowerMock 的情况下对其进行测试,不需要进行太多更改。
  • 你建议改变什么?
  • 提示:另一个更理智的想法,而不是使用 PowerMock 是简单地在你的方法中进行新的调用。你看,如果你创建了难以测试的代码;那么当它很难测试时不要感到惊讶。见youtube.com/playlist?list=PLD0011D00849E1B79

标签: java junit mocking mockito powermock


【解决方案1】:

您还必须添加在 @PrepareForTest 下创建新对象的类,因为您使用的是 whenNew。

你的课好像是 HttpClient

@PrepareForTest({ URL.class, Scanner.class ,HttpClient.class})

解决了您的测试中的另一个问题。 添加了缺失的期望。

PowerMockito.when(scanner.useDelimiter("\\A")).thenReturn(scanner);

更正的工作代码:

来源:

public class HttpClient {

    public String getResponse(String connectionUrl) throws HttpException {

        HttpURLConnection connection = null;

        try {
            URL url = new URL(connectionUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            int responseCode = connection.getResponseCode();
            if (responseCode != 200) {
                throw new HttpException("Response code was " + responseCode
                        + " (should be 200)");
            }

            Scanner scanner = new Scanner(connection.getInputStream())
                    .useDelimiter("\\A");
            String response = scanner.hasNext() ? scanner.next() : "";

            connection.disconnect();
            scanner.close();

            return response;

        } catch (IOException e) {

            if (connection != null) {
                connection.disconnect();
            }

            throw new HttpException(e.getMessage(), e.getCause());
        }
    }

}

class HttpException extends Exception {

    public HttpException(String string) {
        super(string);
    }

    public HttpException(String message, Throwable cause) {
        super(message, cause);
    }

}

测试用例:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ URL.class, Scanner.class, HttpClient.class })
public class HttpClientTest {

    private static final String REQUEST_URL = "http://wwww.google.com";

    @Test
    public void getResponse_NormalResponse() throws Exception {

        String expectedResponse = "This is the expected response text!";
        URL url = PowerMockito.mock(URL.class);
        HttpURLConnection connection = PowerMockito
                .mock(HttpURLConnection.class);
        InputStream inputStream = PowerMockito.mock(InputStream.class);
        Scanner scanner = PowerMockito.mock(Scanner.class);

        PowerMockito.whenNew(URL.class).withArguments(REQUEST_URL)
                .thenReturn(url);
        PowerMockito.whenNew(Scanner.class).withArguments(inputStream)
                .thenReturn(scanner);
        PowerMockito.when(scanner.useDelimiter("\\A")).thenReturn(scanner);

        PowerMockito.when(url.openConnection()).thenReturn(connection);

        // Response code mocked here
        PowerMockito.when(connection.getResponseCode()).thenReturn(200);

        PowerMockito.when(connection.getInputStream()).thenReturn(inputStream);
        PowerMockito.when(scanner.hasNext()).thenReturn(true);
        PowerMockito.when(scanner.next()).thenReturn(expectedResponse);

        HttpClient httpClient = new HttpClient();
        String actualResponse = httpClient.getResponse(REQUEST_URL);

        Assert.assertEquals("Response is wrong!", expectedResponse,
                actualResponse);
    }
}

附上我用过的pom。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>MockItToExample</groupId>
    <artifactId>MockItToExample</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>

        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>1.6.3</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>1.6.3</version>
        </dependency>

    </dependencies>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

【讨论】:

  • 我已经实现了你的代码,但它对我不起作用。它对您有用的原因是因为您指定了一个确实存在的 url (google.com)。连接没有被模拟,测试确实连接到服务器。但我想模拟 http 连接,这样即使没有互联网连接也可以进行测试。这不可能吗?
  • 您好尝试使用私有静态最终字符串 REQUEST_URL = "test.com";它仍然有效。
  • 您得到预期的响应了吗? (“这是预期的响应文本”)。您使用什么测试运行器运行测试?
  • 是的,我得到了预期的结果。将附上屏幕截图。
  • 我也添加了项目的pom。我在 Eclipse 中运行它。
【解决方案2】:

用以下方式注释您的测试类:

@PrepareForTest({URL.class, Scanner.class})

当您想模拟对象创建时,您需要为每种情况添加它(对于您使用whenNew 的每个类)。

【讨论】:

  • 谢谢,我加了行,但测试的结果还是一样
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-04
  • 2015-07-17
  • 1970-01-01
相关资源
最近更新 更多