如何在Java中模拟web服务器进行单元测试?

如何在Java中模拟web服务器进行单元测试?,java,junit,Java,Junit,我想使用模拟web服务器创建一个单元测试。是否有一个用Java编写的web服务器可以从JUnit测试用例轻松启动和停止?尝试使用。Try()它很容易嵌入到单元测试中。以RoundTripTest和Simple编写的示例为例。提供如何将服务器嵌入测试用例的示例 同样,Simple比Jetty轻得多,速度快得多,没有依赖性。因此,您不必在类路径中添加几个jar文件。您也不必关心WEB-INF/WEB.xml或任何其他工件。您正在尝试使用WEB服务器吗 对于模拟web服务器,请尝试使用或类似的方法,只

我想使用模拟web服务器创建一个单元测试。是否有一个用Java编写的web服务器可以从JUnit测试用例轻松启动和停止?

尝试使用。

Try()它很容易嵌入到单元测试中。以RoundTripTest和Simple编写的示例为例。提供如何将服务器嵌入测试用例的示例

同样,Simple比Jetty轻得多,速度快得多,没有依赖性。因此,您不必在类路径中添加几个jar文件。您也不必关心
WEB-INF/WEB.xml
或任何其他工件。

您正在尝试使用WEB服务器吗

对于模拟web服务器,请尝试使用或类似的方法,只需模拟
HttpServletRequest
HttpServletResponse
对象,如:

MyServlet servlet = new MyServlet();
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);

StringWriter out = new StringWriter();
PrintWriter printOut = new PrintWriter(out);
when(mockResponse.getWriter()).thenReturn(printOut);

servlet.doGet(mockRequest, mockResponse);

verify(mockResponse).setStatus(200);
assertEquals("my content", out.toString());

对于嵌入式web服务器,您可以使用,您可以使用。

另一个好的选择是;它提供了一个流畅的界面,您可以使用该界面定义模拟web服务器的行为。

似乎提供了一组可靠的存根和模拟,用于测试外部web服务

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);


@Test
public void exactUrlOnly() {
    stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withBody("Hello world!")));

    assertThat(testClient.get("/some/thing").statusCode(), is(200));
    assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
它还可以与spock集成。找到了示例。

您可以尝试使用一个库,该库具有流畅的编程Java API,用于在测试中存根和模拟http资源。例如:

onRequest()
    .havingMethodEqualTo("GET")
    .havingPathEqualTo("/accounts/1")
    .havingBody(isEmptyOrNullString())
    .havingHeaderEqualTo("Accept", "application/json")
.respond()
    .withDelay(2, SECONDS)
    .withStatus(200)
    .withBody("{\\"account\\":{\\"id\\" : 1}}")
    .withEncoding(Charset.forName("UTF-8"))
    .withContentType("application/json; charset=UTF-8");

您还可以使用JDK的
com.sun.net.httpserver.httpserver
类编写模拟(不需要外部依赖项)。请参见详细说明如何

总之:

HttpServer httpServer = HttpServer.create(new InetSocketAddress(8000), 0); // or use InetSocketAddress(0) for ephemeral port
httpServer.createContext("/api/endpoint", new HttpHandler() {
   public void handle(HttpExchange exchange) throws IOException {
      byte[] response = "{\"success\": true}".getBytes();
      exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length);
      exchange.getResponseBody().write(response);
      exchange.close();
   }
});
httpServer.start();

try {
// Do your work...
} finally {
   httpServer.stop(0); // or put this in an @After method or the like
}

如果您使用的是ApacheHttpClient,这将是一个很好的选择。

HttpClientMock-HttpClientMock=new-HttpClientMock()
HttpClientMock(“http://example.com:8080"); 
httpClientMock.onGet(“/login?user=john”).doReturnJSON(“{permission:1}”);

基本上,您可以在mock对象上发出请求,然后对它执行一些验证
httpClientMock.verify().get(“http://localhost/loginwithParameter(“user”,“john”).called()

为了完整起见,还对jetty进行了包装,以使其更易于使用

让您的测试类扩展CamelTestSupport,然后定义一个路由,例如:

  @Override
  protected RouteBuilder createRouteBuilder() {
    return new RouteBuilder() {
      @Override
      public void configure() {
        from("jetty:http://localhost:" + portToUse).process(
                new Processor() {
                  @Override
                  public void process(Exchange exchange) throws Exception {
                    // Get the request information.
                    requestReceivedByServer = (String) exchange.getIn().getHeader(Exchange.HTTP_PATH);

                    // For testing empty response
                    exchange.getOut().setBody("your response");
                    ....
获取它的示例maven依赖项:

<dependency> <!-- used at runtime, by camel in the tests -->
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jetty</artifactId>
  <version>2.12.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-core</artifactId>
  <version>2.12.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-test</artifactId>
  <version>2.12.1</version>
  <scope>test</scope>
</dependency>

org.apache.camel
骆驼码头
2.12.1
测试
org.apache.camel
驼芯
2.12.1
测试
org.apache.camel
骆驼试验
2.12.1
测试
我推荐。它是模拟真实服务的极好工具,因为它允许在测试中进行状态断言(服务器端断言)



也可以使用。但这会导致难以维护行为测试(验证客户端调用是否符合预期)。

虽然这可能是用户想要的,但这不是“模拟”web服务器,而是在单元测试中启动的实际web服务器。例如,如果端口被占用,它将失败,因此不是真正的模拟(即具有来自系统的外部依赖)。根据Martin Fowler的命名法fore,这是一个“赝品”。这就是说,这正是我想要的。这个项目是否已经停止?这个任务看起来更像是创建集成测试,而不是单元测试。如果你把“单元测试”理解为测试技术(“JUnit”)和质量(快速、无先决条件、测试用例孤立),而不是规模(只测试一个单元),那么“集成测试”可以是“单元测试”.不确定为什么该问题因不符合SO准则而被关闭。我似乎很清楚OP在问什么。关于这是一个单元测试还是集成测试,以及为测试目的而构建的真正web服务器是否可以被视为一个模拟测试,我们是否在吹毛求疵?我尝试使用Jadler,但每当我试图调用
initJadler()
时,我都会遇到前面提到的错误。你有没有想过会出什么问题?你能在这里粘贴一个mvn dependency:tree的结果吗?这可能是库冲突。请阅读我在github.com/jadler-mocking/jadler/issues/107中的答案,问题是由Jetty版本冲突(8对9)引起的。您可以删除Jetty9依赖项(如果测试运行不需要该依赖项),也可以在不使用Jetty依赖项的情况下运行Jadler()。看起来此框架不支持TestNG。请参阅错误报告已关闭为不可恢复。你绝对可以将Jadler与TestNG结合使用。这对我来说非常有效。使用wireMockConfig().dynamicPort()可以在其他程序可能使用该端口的设置中更容易预测。我希望它是默认的,因为我几乎错过了这种可能性,跳过了这个伟大的库。@gribo我想testClient是您正在测试的实际实例。而且MockServer依赖于Netty(而不是Jetty)。是否有方法断言
HttpServer
是否收到请求?不应使用com.sun.*类。@RamPatra您可以在处理程序类中跟踪它(它们请求的内容等)@TilmanHausherr com.sun.*可以。不应使用“sun.*”或“jdk.internal.*”。HttpClientMock是否可以延迟返回正确的响应,或者仅在几次重试后返回?这与Jadler相比如何?你能给出一个类似于Jadler的示例代码吗?