2

我有以下类,其中包含一个永远不会更改的硬编码 URL:

    public class HttpClient {
        private final String DOWNLOAD_URL = "http://original.url.json";

        public String readJsonDataFromUrl() throws IOException {
            URLConnection urlConnection = getUrlConnection();

            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            StringBuffer content = new StringBuffer();

            String readLine = "";
            while ((readLine = reader.readLine()) != null)  {
                content.append(readLine);
            }

            return content.toString();
        }

        private URLConnection getUrlConnection() throws IOException {
            URL jsonLocator = new URL(DOWNLOAD_URL);

            return jsonLocator.openConnection();
        }
    }

现在想象一下,我希望在我的测试中出现 IOException。在我看来,这样做的唯一方法是在模拟对象中重写完整的类,因为 final 变量:

public class HttpClientMock extends HttpClient  {
    private final String DOWNLOAD_URL = "http://wrong.test.url.json";

    @Override
    public String readJsonDataFromUrl() throws IOException {
        URLConnection urlConnection = getUrlConnection();

        BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
        StringBuffer content = new StringBuffer();

        String readLine = "";
        while ((readLine = reader.readLine()) != null)  {
            content.append(readLine);
        }

        return content.toString();
    }

    private URLConnection getUrlConnection() throws IOException {
        URL jsonLocator = new URL(DOWNLOAD_URL);
        URLConnection urlConnection = jsonLocator.openConnection();

        return urlConnection;
    }
}

但这有点牵强。如果更改原始方法,测试结果可能仍然是肯定的,因为通过此尝试,我实际上不再测试原始类。

如何正确地做到这一点?(我不想只为这个测试使用一个框架,那么有没有设计尝试以一种通用的方式解决这个问题?)

4

2 回答 2

0

Gilbert Le Blanc 的建议的另一个变化是,通过构造函数注入 URL,使 HttpClient 完全不知道 URL。

 public class HttpClient {
   private final String url;

   public HttpClient(String url) { this.url = url; }

 }

您可以在 HttpClient 外部某处硬编码 URL(或从配置中读取),并将其注入到您实例化客户端的任何位置。然后在您的测试中,注入一个错误的 url 将是微不足道的。

于 2012-09-07T19:32:54.347 回答
0

感谢大家,但我认为 Gilbert Le Blanc 的解决方案对于看起来像这样的情况是最可取的:

原班:

public class HttpClient {
    private final String DOWNLOAD_URL = "http://my.original.json.url";

    public String readJsonDataFromUrl() throws IOException {
        URLConnection urlConnection = getUrlConnection();

        BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
        StringBuffer content = new StringBuffer();

        String readLine = "";
        while ((readLine = reader.readLine()) != null)  {
            content.append(readLine);
        }

        return content.toString();
    }

    private URLConnection getUrlConnection() throws IOException {
        URL jsonLocator = new URL(getConnectionString());

        return jsonLocator.openConnection();
    }

    protected String getConnectionString()  {
        return DOWNLOAD_URL;
    }
}

模拟对象:

public class HttpClientMock extends HttpClient  {
    private String downloadUrl = "http://my.original.json.url";

    public HttpClientMock()  {
        super();
    }

    public HttpClientMock(String downloadUrl)  { 
        this.downloadUrl = downloadUrl;
    }

    @Override
    protected String getConnectionString()  {
        return downloadUrl;
    }
}

和工作测试:

public class HttpClientTest {

    private JSONParser jsonParser = new JSONParser();

    @Test
    public void readJsonDataFromUrlSucceeds() throws IOException, ParseException {
        HttpClient httpClient = new HttpClientMock();

        String jsonString = httpClient.readJsonDataFromUrl();
        JSONObject jsonObject = (JSONObject)jsonParser.parse(jsonString);

        assertTrue(jsonObject.size() > 0);
    }

    @Test(expected = IOException.class)
    public void readJsonDataFromMalformedUrlFails() throws IOException, ParseException {
        HttpClient httpClient = new HttpClientMock("http://malformed");

        httpClient.readJsonDataFromUrl();
    }
}
于 2012-09-07T19:52:10.510 回答