测试 URL 和 URL 连接

Testing URL and URLConnection

我不知道如何在没有真正连接到真正的 url 到服务器的情况下测试它。

我已经阅读了一些关于在这种情况下使用 Mockito 的文章并尝试四处搜索,但找不到好的教程或建议我应该如何为 URL 和 [=58= 进行 jUnit 测试]我项目中的连接。

这是我在尝试测试时遇到问题的代码:

public JSONObject getJSONObj()
        throws MalformedURLException, IOException, ParseException {
    String jsonString;
    try (InputStream is = getURLConnection("RealUrlStringGoesHere").getInputStream();) {
        jsonString = IOUtils.toString(is);
    }
    return (JSONObject) JSONValue.parseWithException(jsonString);
}

public URLConnection getURLConnection(String urlStr) throws MalformedURLException, IOException {
    URL url = new URL(urlStr);
    URLConnection conn = url.openConnection();
    return conn;
}

这里也有我用的导入,如果有人想知道:

import java.net.URLConnection;
import org.apache.commons.io.IOUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;

已编辑

感谢您的回答,但我似乎对此完全迷失了。也许我想的太复杂了,但是单元测试对我来说是个新东西,但真的很想学习更多。

是的,我尝试测试 getJSONObj 方法,但是那些 URL 和 URL 连接让我很难理解如何通过 "faking" 来测试我的方法相信这真的需要联系。

无法理解您的真正意思,所以这是我尝试按照您所说的 Jeff Bowman 执行操作时的当前代码。 (仍在使用那个大字符串,因为我试图先用当前样式完成它,然后在工作后用 Reader 获得更好的性能。)

Discusser.java

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.apache.commons.io.IOUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;

public class Discusser implements URLOpener {

    public JSONObject getJSONObj() throws IOException, ParseException {
        String jsonString;
        try (InputStream is = openURL("RealUrlStringGoesHere");) {
            jsonString = IOUtils.toString(is);
        }
        return (JSONObject) JSONValue.parseWithException(jsonString);
    }

    @Override
    public InputStream openURL(String urlStr) throws IOException {
        URL url = new URL(urlStr);
        URLConnection urlConnection = url.openConnection();
        return urlConnection.getInputStream();
    }
}

URLOpener.java

import java.io.IOException;
import java.io.InputStream;

public interface URLOpener {
    InputStream openURL(String urlStr) throws IOException;
}

这个测试几乎没有用,因为我认为我尝试使用模拟的方式是完全错误的。 (当 discusser.getJSONObj() 时返回 null)

DiscusserTest.java

import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import org.json.simple.JSONObject;
import org.junit.Test;
import org.mockito.Mockito;

public class DiscusserTest {

    @Test
    public void testGetJSONObj() throws Exception {
        JSONObject expectedJSONObject = createExpected();
        ByteArrayInputStream inputForMock = new ByteArrayInputStream(generateJSONString().getBytes("UTF-8"));
        // Should I mock like this or...
        Discusser discusser = Mockito.mock(Discusser.class);
        Mockito.when(discusser.openURL("asd")).thenReturn(inputForMock);
        //
        assertEquals(expectedJSONObject, discusser.getJSONObj());
    }

    private String generateJSONString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"id\":\"123\",");
        sb.append("\"name\":\"test\"");
        sb.append("}");
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    private JSONObject createExpected() {
        JSONObject obj = new JSONObject();
        obj.put("id", 123);
        obj.put("name", "test");
        return obj;
    }
}

您或其他人能否提供指导/示例,说明应该如何测试讨论器中的 getJSONObj() 方法?

您可以在测试中启动一个服务器并针对该服务器进行测试。您可以为此使用 MockServer

你想测试什么?

如果您尝试测试与真实服务器的交互是否正确发生,那么再多的模拟也无济于事。您想编写一个集成测试。

如果您尝试测试通过 Java 的 URL 连接发生的交互,那么模拟服务器可能会工作 。不过,我认为这不一定是有用的东西,因为 Java URLConnection 框架是经过充分测试的第三方代码。

看起来这里最可测试的组件是 getJSONObj(),其中不太可测试的部分是将 URL 转换为 InputStream 的函数。让它成为你的界面,而不是:

interface URLOpener {
  InputStream openURL(String url);
}

到那时,您可以在生产代码中使用非常简单的实际实现,或者传入一个非常简单的模拟,returns 一个 ByteArrayInputStream。


旁注:如果使用 JSONValue.parse(Reader) 而不是尝试构造一个包含整个 JSON 文件的大字符串,您可能会发现性能更好。这不会干扰模拟,因为您可以改用 StringReader。

/* in prod, within your actual URLOpener */
return new InputStreamReader(urlConnection.getInputStream());

/* in test, for your mock URLOpener */
when(mockUrlOpener.openURL(expectedURL)).thenReturn(new StringReader(testJSON));

JSONValue value = JSONValue.parse(new BufferedReader(readerFromUrl));

我设法让它工作,这是结果。如果您有改进的想法或其他建议,我很高兴得到它们。

我在 Discusser 中为 URLOpener 添加了 setter,这样我就可以很容易地把那个模拟的放在那里。

Discusser.java

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;

public class Discusser implements URLOpener {

    private URLOpener urlOpener;

    public JSONObject getJSONObj() throws IOException, ParseException {
        JSONObject jsonObj;
        try (InputStream is = openURL("RealUrlStringGoesHere");) {
            jsonObj = (JSONObject) JSONValue.parse(new InputStreamReader(is));
        }
        return jsonObj;
    }

    @Override
    public InputStream openURL(String urlStr) throws IOException {
        return urlOpener.openURL(urlStr);
    }

    public void setURLOpener(URLOpener urlOpener) {
        this.urlOpener = urlOpener;
    }

}

DiscusserTest.java

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.json.simple.JSONObject;
import org.junit.Test;

public class DiscusserTest {

    @Test
    public void testGetJSONObj() throws Exception {
        Discusser discusser = new Discusser();
        discusser.setURLOpener(createMockURLOpener());
        assertEquals(createExpected(), discusser.getJSONObj());
    }

    private URLOpener createMockURLOpener() throws IOException {
        URLOpener mockUrlOpener = mock(URLOpener.class);
        ByteArrayInputStream input = new ByteArrayInputStream(generateJSONString().getBytes("UTF-8"));
        when(mockUrlOpener.openURL("RealUrlStringGoesHere")).thenReturn(input);
        return mockUrlOpener;
    }

    private String generateJSONString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"id\":\"123\",");
        sb.append("\"name\":\"test\"");
        sb.append("}");
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    private JSONObject createExpected() {
        JSONObject obj = new JSONObject();
        obj.put("id", "123");
        obj.put("name", "test");
        return obj;
    }
}

URLOpener.java

import java.io.IOException;
import java.io.InputStream;

public interface URLOpener {
    InputStream openURL(String urlStr) throws IOException;
}