【问题标题】:mock https request in java在java中模拟https请求
【发布时间】:2011-04-20 22:37:45
【问题描述】:

假设我正在编写一个应用程序,我需要能够执行以下操作:

String url = "https://someurl/";
GetMethod method = new GetMethod(URLEncoder.encode(url));
String content = method.getResponseBodyAsString();

有没有办法提供一个可以让我处理 https 请求的模拟服务器?我正在寻找一种编写单元测试的方法,但我需要能够模拟实际输出到https://someurl 的部分,以便我可以得到一个已知的响应。

【问题讨论】:

标签: java http mocking


【解决方案1】:

看看 jadler (http://jadler.net),一个我已经研究了一段时间的 http stubbing/mocking 库。 1.0.0 稳定版刚刚发布,应该可以提供你要求的功能:

@Test
public void getAccount() {

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

    final AccountService service = new AccountServiceRestImpl("http", "localhost", port());
    final Account account = service.getAccount(1);

    assertThat(account, is(notNullValue()));
    assertThat(account.getId(), is(1));
}


@Test
public void deleteAccount() {

    onRequest()
        .havingMethodEqualTo("DELETE")
        .havingPathEqualTo("/accounts/1")
    .respond()
        .withStatus(204);

    final AccountService service = new AccountServiceRestImpl("http", "localhost", port());
    service.deleteAccount(1);

    verifyThatRequest()
        .havingMethodEqualTo("DELETE")
        .havingPathEqualTo("/accounts/1")
    .receivedOnce();
}

【讨论】:

    【解决方案2】:

    你基本上有两个选择:

    1.抽象对框架的调用并对其进行测试。

    例如重构代码以允许您在某个时候注入模拟实现。有很多方法可以做到这一点。例如创建一个 getUrlAsString() 并模拟它。 (上面也建议过)。或者创建一个返回 GetMethod 对象的 url getter 工厂。然后可以模拟工厂。

    2。启动应用服务器作为测试的一部分,然后针对它运行您的方法。 (这将更像是一个集成测试)

    这可以通过多种方式实现。这可以在测试之外,例如maven 码头插件。或者测试可以以编程方式启动服务器。见:http://docs.codehaus.org/display/JETTY/Embedding+Jetty

    通过 https 运行它会使这变得复杂,但使用自签名证书仍然可以。但我会问自己——你到底想测试什么?我怀疑您是否真的需要测试 https 功能,它是一项经过验证的技术。

    我个人会选择选项 1 - 您正在尝试测试外部库的功能。这通常是不必要的。将您的依赖项抽象到外部库也是一个好习惯。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      如果您正在编写单元测试,则不需要任何外部依赖项。来自api,

      GetMethod 
      

      扩展

      HttpMethod
      

      因此您可以使用自己喜欢的模拟库轻松模拟它。你的

      method.getResponseBodyAsString()
      

      可以模拟调用以返回您想要的任何数据。

      【讨论】:

      • 但是 GetMethod 是在代码本身中创建的,而不是在我的单元测试中,所以我无法模拟它。
      • 我不明白,你不控制你正在测试的代码?
      • 我控制代码,但 GetMethod 对象是在我正在测试的方法内部创建的,因此我无法传入模拟对象。模拟的工作方式是您事先设置它并将其传递给您正在测试的任何方法,但在这种情况下,GetMethod 对象是在方法本身内部创建的,所以我不能将它传递进去。如果是不清楚,请告诉我。
      • @dcp,啊,现在。好吧,显而易见的答案是重构你注入或只是设置你想要的 HttpMethod ......这可能比使用一些外部依赖项访问网络更好,这会意外失败。最好在测试中保持一致性...
      • 不知道你所说的“反射给你注入或只是设置你想要的HttpMethod”是什么意思,你能详细说明一下吗?我的另一个想法是为 Http 客户端创建一个模拟库,然后我可以做我想做的事情(即我会为 GetMethod 创建自己的模拟实现等)。
      【解决方案4】:

      您可以将该代码包装在某个类中并使用 WebClient.getUrl() 然后模拟(例如jmock)该方法以返回存储的文件 - 比如说

      expectation {
         oneOf("https://someurl/"), will(returnValue(someHTML));
      }
      

      【讨论】:

        【解决方案5】:

        看看JWebUnithttp://jwebunit.sourceforge.net/

        这是一个测试示例...它真的很直观。

        public class ExampleWebTestCase extends WebTestCase {
            public void setUp() {
                super.setUp();
                setBaseUrl("http://localhost:8080/test");
            }
        
            public void test1() {
                beginAt("/home");
                clickLink("login");
                assertTitleEquals("Login");
                setTextField("username", "test");
                setTextField("password", "test123");
                submit();
                assertTitleEquals("Welcome, test!");
            }
        }
        

        【讨论】:

        • 是的,我熟悉 JWebUnit,但在这里我需要能够模拟实际提供的 url。 JWebUnit 在您调用的网站/服务实际运行时工作。
        【解决方案6】:

        您始终可以启动 thttpd 服务器作为单元测试的一部分,以在本地处理请求。不过,理想情况下,您有一个经过良好测试的 GetMethod,然后您可以模拟它,而不必为您的所有测试实际使用远程服务器。

        资源

        【讨论】:

          【解决方案7】:

          您对模拟这个“Get”调用有什么兴趣,因为如果您正在寻找一个通用的 Java 模拟框架,它与 JUnit 很好地集成并允许设置在合并到 JUnit 套件时自动断言的期望,那你真的应该看看jMock

          现在没有更多代码,很难确定这是否真的是您正在寻找的内容,但是与您编写的示例代码类似的(有点无用的)示例会是这样的:

          class GetMethodTest {
          @Rule public JUnitRuleMockery context = new JunitRuleMockery();
          
          @Test
          public void testGetMethod() throws Exception {
              // Setup mocked object with expectations
              final GetMethod method = context.mock(GetMethod.class);
              context.checking(new Expectations() {{
                  oneOf (method).getResponseBodyAsString();
                  will(returnValue("Response text goes here"));
              }});
          
              // Now do the checking against mocked object
              String content = method.getResponseBodyAsString();
          }
          }
          

          【讨论】:

          • 嗯,那么我可能会考虑使用像 Object Teams 这样的 Java 扩展框架,它能够将特定的方法调用重新路由到您自己定义的方法,因此您可以利用控制流的精确点你需要改变,注入你自己的代码或你的模拟对象。无论是公共方法还是私有方法,ObjecTeams 都充当 Java 的超集,因此您可以做 Java 通常无法做到的事情。
          【解决方案8】:

          使用xml模拟存根服务器,可以根据请求参数、标头等模拟静态http响应,配置和使用非常简单。

          http://xmlmimic.sourceforge.net/ http://sourceforge.net/projects/xmlmimic/

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-06-07
            • 1970-01-01
            • 1970-01-01
            • 2012-08-02
            • 2017-06-18
            • 2011-12-30
            • 2012-03-11
            • 1970-01-01
            相关资源
            最近更新 更多