如何使用 mockito 测试 REST 服务?

2022-01-14 00:00:00 rest unit-testing mockito java

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

I am very new in Java Unit Testing and I heard that Mockito framework is really good for testing purposes.

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

I have developed a REST Server (CRUD methods) and now I want to test it, but I don't know how?

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

Even more I don't know how this testing procedure should begin. My server should work on localhost and then make calls on that url(e.g. localhost:8888)?

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

Here is what I tried so far, but I'm pretty sure that this isn't the right way.

    @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).如果一切顺利,服务器应回复您的架构已成功创建!".

In this test case I want to send a Json string to the server(POST). If everything went well then the server should reply with "Your schema was succesfully created!".

有人可以帮帮我吗?

推荐答案

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

OK. So, the contract of the method is the following: Parse the input string as JSON, and send back BAD_REQUEST if it's invalid. If it's valid, create an entity in the datastore with various properties (you know them), and send back OK.

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

And you need to verify that this contract is fulfilled by the method.

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

Where does Mockito help here? Well, if you test this method without Mockito, you need a real DataStoreService, and you need to verify that the entity has been created correctly in this real DataStoreService. This is where your test is not a unit test anymore, and this is also where it's too complex to test, too long, and too hard to run because it needs a complex environment.

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

Mockito can help by mocking the dependency on the DataStoreService: you can create a mock of DataStoreService, and verify that this mock is indeed called with the appropriate entity argument when you call your initialize() method in your test.

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

To do that, you need to be able to inject the DataStoreService into your object under test. It can be as easy as refactoring your object in the following way:

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
    }
}

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

And now in your test method, you can do:

@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
}

相关文章