【问题标题】:How to use mockito for testing a REST service?如何使用 mockito 测试 REST 服务?
【发布时间】:2012-05-27 15:48:57
【问题描述】:

我是 Java 单元测试的新手,听说 Mockito 框架非常适合用于测试目的。

我已经开发了一个 REST 服务器(CRUD 方法),现在我想测试它,但我不知道怎么做?

我更不知道这个测试程序应该如何开始。我的服务器应该在 localhost 上运行,然后在该 url 上进行调用(例如 localhost:8888)?

这是我目前尝试的方法,但我很确定这不是正确的方法。

    @Test
    public void testInitialize() {
        RESTfulGeneric rest = mock(RESTfulGeneric.class);

        ResponseBuilder builder = Response.status(Response.Status.OK);

        builder = Response.status(Response.Status.OK).entity(
                "Your schema was succesfully created!");

        when(rest.initialize(DatabaseSchema)).thenReturn(builder.build());

        String result = rest.initialize(DatabaseSchema).getEntity().toString();

        System.out.println("Here: " + result);

        assertEquals("Your schema was succesfully created!", result);

    }

这是initialize 方法的代码。

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/initialize")
    public Response initialize(String DatabaseSchema) {

        /** Set the LogLevel to Info, severe, warning and info will be written */
        LOGGER.setLevel(Level.INFO);

        ResponseBuilder builder = Response.status(Response.Status.OK);

        LOGGER.info("POST/initialize - Initialize the " + user.getUserEmail()
                + " namespace with a database schema.");

        /** Get a handle on the datastore itself */
        DatastoreService datastore = DatastoreServiceFactory
                .getDatastoreService();


        datastore.put(dbSchema);

        builder = Response.status(Response.Status.OK).entity(
                "Your schema was succesfully created!");
        /** Send response */
        return builder.build();
    }

在这个测试用例中,我想向服务器发送一个 Json 字符串(POST)。如果一切顺利,服务器应回复“您的架构已成功创建!”。

有人可以帮帮我吗?

【问题讨论】:

  • 在上面的代码中,你在哪里向服务器发送了一个 JSON 字符串?为什么你甚至需要 Mockito 来进行这样的测试?
  • 我不知道我是否需要 Mockito 来进行这些测试,因为我说过我正在尝试了解如何测试 REST 服务。当我调用这个方法时,JSON 文件正在发送:rest.initialize(DatabaseSchema)DatabaseSchema 是一个 JSON 文件。
  • 不,它没有。 rest 是一个模拟。它是一个回答你告诉它回答什么的对象。这不是你的休息服务器。您正在测试 Mockito 而不是测试您的服务器。您不需要 Mockito 进行此类测试。
  • 谢谢!你能指导我吗,我该怎么做?我想测试我的 CRUD 方法。我想要单元测试,以测试我服务器上的每个休息方法。
  • 要么进行集成测试并将 JSON 实际发送到服务器并分析得到的答案以确保它们是合适的,要么进行单元测试以测试在服务器端使用的类的方法。 Mockito 对后者很有用。但是如果没有看到你的代码,就不可能告诉你如何测试它,除了指向你的 Mockito 的文档。

标签: java unit-testing rest mockito


【解决方案1】:

好的。因此,该方法的约定如下:将输入字符串解析为 JSON,如果无效则返回BAD_REQUEST。如果有效,在datastore 中创建一个具有各种属性(你知道它们)的实体,然后发回OK

并且您需要验证该方法是否履行了此合同。

Mockito 在这方面有何帮助?好吧,如果你在没有 Mockito 的情况下测试这个方法,你需要一个真实的DataStoreService,并且你需要验证实体已经在这个真实的DataStoreService 中正确创建。这就是您的测试不再是单元测试的地方,这也是测试太复杂、太长、太难运行的地方,因为它需要一个复杂的环境。

Mockito 可以通过模拟对 DataStoreService 的依赖来提供帮助:您可以创建 DataStoreService 的模拟,并在您在测试中调用 initialize() 方法时验证该模拟确实是使用适当的实体参数调用的.

为此,您需要能够将DataStoreService 注入到您的测试对象中。它可以像通过以下方式重构对象一样简单:

public class MyRestService {
    private DataStoreService dataStoreService;

    // constructor used on the server
    public MyRestService() {
        this.dataStoreService = DatastoreServiceFactory.getDatastoreService();
    }

    // constructor used by the unit tests
    public MyRestService(DataStoreService dataStoreService) {
        this.dataStoreService = dataStoreService;
    }

    public Response initialize(String DatabaseSchema) {
         ...
         // use this.dataStoreService instead of datastore
    }
}

现在在你的测试方法中,你可以这样做:

@Test
public void testInitializeWithGoodInput() {
    DataStoreService mockDataStoreService = mock(DataStoreService.class);
    MyRestService service = new MyRestService(mockDataStoreService);
    String goodInput = "...";
    Response response = service.initialize(goodInput);
    assertEquals(Response.Status.OK, response.getStatus());

    ArgumentCaptor<Entity> argument = ArgumentCaptor.forClass(Entity.class);
    verify(mock).put(argument.capture());
    assertEquals("the correct kind", argument.getValue().getKind());
    // ... other assertions
}

【讨论】:

  • 谢谢!我有一个问题:这里应该是 verify(mockDataStoreService).put(argument.capture()); ?即使在我更改为mockDataStoreService 后确实如此,我也会收到错误The method verify(DatastoreService) is undefined for the type MyRestService
  • 测试方法应该在单元测试类中,而不是在你测试的类中。 EasyMock 通常与import static org.mockito.Mockito.* 一起使用。 verify 是 Mockito 类的静态方法。
【解决方案2】:

您所说的听起来更像是integration testing,而 Mockito(或任何其他模拟框架)对您没有多大用处。

如果您想对自己编写的代码进行单元测试,Mockito 无疑是一个有用的工具。

我建议您阅读更多关于模拟/单元测试以及应该在哪些情况下使用它的信息。

【讨论】:

  • 我完全不能同意这个答案。我在大量的集成测试中使用了 Mockito。通常,在集成测试中,整个子系统是不相关的,可以合理地模拟。当您想要测试系统的某个部分时,Mockito(或其他一些模拟框架)通常很有用,而其他部分的交互不会影响测试中发生的事情。这可能是单元测试、批量测试或集成测试。
  • @DavidWallace 同意。我的措辞具有误导性,我的意思是,对于 OP 所描述的内容(部署他的应用程序并向其发出 REST 请求),模拟不会有太大用处。
【解决方案3】:

Mockito(通常)用于测试代码的部分;例如,如果您正在使用您的 REST 服务,但不想进行全栈测试,您可以模拟连接到 REST 服务的服务,让您能够准确、一致地测试特定行为。

要在不访问数据库的情况下测试 REST 服务的内部部分(例如,特定的服务方法),您可以模拟 DB 子系统,只允许测试服务内部,而不涉及数据库。该测试属于 REST 服务模块,而不是客户端。

要测试 REST 服务本身,您需要使用一个实际的客户端库,创建一个全栈集成测试。 Mockito 可以在这里用来模拟与 REST 服务消费无关的客户端部分。

【讨论】:

  • 感谢您的回复。实际上我需要的是测试我开发的 REST 服务,所以我想测试个别方法,但我不知道如何。例如,正如我在问题中所说,我想将 JSON 发送到服务器并验证响应。我不知道我应该在哪里提出请求,在本地主机上或我在哪里部署了 API。我正在使用 GAE 作为后端。
  • @IonMorozan 不确定我是否理解这个问题——你可以在本地测试,你可以测试部署,这取决于你想测试哪个。无论端点如何,该请求都可能在本地发出
【解决方案4】:

最好的方法是使用wiremock 添加以下依赖项 com.github.tomakehurst wiremock 2.4.1 org.igniterealtime.smack smack-core 4.0.6

如下所示定义和使用wiremock

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

String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
        .willReturn(aResponse().withStatus(200)
                .withHeader("Content-Type", "application/json").withBody(response)));

【讨论】:

    【解决方案5】:

    我同意这不是单元测试而是集成测试,无论如何,您宁愿看一下 jersey 和嵌入式 grizzly 服务器测试。总而言之,这段代码在 localhost:8888 启动了 grizzly 服务器(也可以启动数据库),然后设置客户端的 jersey 客户端并发送一个 POST 请求,该请求应该被测试。这是一个集成,因为您正在测试服务器和数据库,但是您可以使用 mockito 来模拟数据库,但这取决于您的服务器和数据库的绑定程度。

    (使用 jersey 1.11 和 grizzly 2.2 进行测试)

        @BeforeClass
        public static void setUpClass() throws Exception {
            // starts grizzly
            Starter.start_grizzly(true);
            Thread.sleep(4000);
        }
    
        @Before
        public void setUp() throws Exception {
            client = new Client();
            webResource = client.resource("http://localhost:8888");
        }   
    
        @Test
        public void testPostSchemaDatabase() throws Exception {
            {
                String DatabaseSchema = "{ database_schema : {...}}";
                logger.info("REST client configured to send: "  + DatabaseSchema);
                ClientResponse response =  
                        webResource
                                 .path("/initialize")
                                 .type("application/json")
                                 .post(ClientResponse.class, DatabaseSchema);
                //wait for the server to process
                Thread.sleep(2000);
                assertEquals(response.getStatus(), 204);    
                //test the response
            }       
        }
    
        @After
        public void after() throws JSONException
        {
                //probably you want to delete the schema at database and stop the server
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-20
      • 1970-01-01
      相关资源
      最近更新 更多