如何正确地对RestEasy响应进行单元测试?

4
我有一个RESTEasy客户端端点,连接到基于REST的数据库服务器,该服务器提供JSON响应。另一端的数据模型(使用JSON注释注释的POJO)可供使用。
我想正确地单元测试正在与请求/响应通信的方法。使用JUnit和Mockito编写测试代码。
如果一个方法发送请求,需要解析JSON结果,应该提取创建实体的新ID并“重用”此ID以使自动生成的设备名称更易读且更友好,则情况变得更加复杂。我想也许这个方法应该分成两个方法,但我看不到任何干净和良好的解决方案,因为这个方法将被用作系统中其他组件的访问点,因此一个方法应该完成一件事。
说了那么多,让我们看看代码本身:
public void createDevice(final Device device) {
    final RequestDescriptor requestDescriptor = new DeviceCreateRequestDescriptor(device);
    final RequestCreator requestCreator = new RequestCreator(requestDescriptor, HttpMethod.POST, uriBuilder);
    final ClientRequest request = requestCreator.create();
    final ClientResponse response = executor.execute(request);
    final String responseString = (String) response.getEntity(String.class);

    //JSON unmarshall logic will be here
    //I should get the device ID from the response, doable with e.g. Jackson
    String deviceId = "";

    changeDeviceName(device, deviceId);
}

   private void changeDeviceName(final Device device, final String deviceId) throws Exception {
    final UriBuilderWrapper uriBuilder = new UriBuilderWrapper(databaseConfig,
            m2mInterfaceContainer.getM2MProvisioningInterface());
    final RequestDescriptor requestDescriptor = new DeviceNameChangeRequestDescriptor(device, deviceId);
    final RequestCreator requestCreator = new RequestCreator(requestDescriptor, HttpMethod.POST, uriBuilder);
    final ClientRequest request = requestCreator.create();
    executor.execute(request);
}

我的测试代码如下:

@Test
public void testCreateDeviceSendsTwoRequests() throws Exception {
    clientExecutorWrapper = mock(ClientExecutorWrapper.class);

    m2mDeviceManager = new M2MDeviceManager();
    device = new Device("123456666", "6789066666");
    m2mDeviceManager.createDevice(device);

    final ArgumentCaptor<ClientRequest> requestCaptor = ArgumentCaptor.forClass(ClientRequest.class);
    verify(clientExecutorWrapper, times(2)).execute(requestCaptor.capture());

    final List<ClientRequest> capturedRequests = requestCaptor.getAllValues();

    final Map<String, String> expectedFormParametersForCreateDeviceRequest = createExpectedFormParametersForFirstRequest();
    final Map<String, String> expectedFormParametersForChangeNameRequest = createExpectedFormParametersForSecondRequest();

    final String expectedUrlCreateDeviceRequest = "dummy_url_1;
    final String expectedUrlForChangeNameRequest = "dummy_url_2";
    RequestChecker.checkRequest(capturedRequests.get(0), expectedFormParametersForCreateDeviceRequest,
            expectedUrlCreateDeviceRequest);
    RequestChecker.checkRequest(capturedRequests.get(1), expectedFormParametersForChangeNameRequest,
            expectedUrlForChangeNameRequest);
}

我的测试用例的辅助方法如下:

private Map<String, String> createExpectedFormParametersForFirstRequest() {
    final Map<String, String> expectedFormParameters = new HashMap<String, String>();
    expectedFormParameters.put("some_param","some_value");
    // ... other form parameters
    return expectedFormParameters;
}

private Map<String, String> createExpectedFormParametersForSecondRequest() {
    final Map<String, String> expectedFormParameters = new HashMap<String, String>();
    expectedFormParameters.put("some_param2","some_value2");
    // ... other form parameters
    return expectedFormParameters;
}

测试中的RequestChecker辅助类如下:

class RequestChecker {

public static void checkRequest(final ClientRequest request, final Map<String, String> expectedFormParameters,
        final String expectedUrl) throws Exception {
    final MultivaluedMap<String, String> formParameters = request.getFormParameters();
    final MultivaluedMap<String, String> queryParameters = request.getQueryParameters();

    assertEquals(expectedUrl, request.getUri());

    assertTrue(queryParameters.size() == 0);
    checkFormParameters(expectedFormParameters, formParameters);

 }

private static void checkFormParameters(final Map<String, String> expectedFormParameters,
        final MultivaluedMap<String, String> formParameters) {
    final Set<Entry<String, String>> expectedParameters = expectedFormParameters.entrySet();
    for (final Entry<String, String> expectedParameter : expectedParameters) {
        final String expectedParameterName = expectedParameter.getKey();
        final String expectedParameterValue = expectedParameter.getValue();
        final List<String> actualFormParameters = formParameters.get(expectedParameterName);
        checkFormParameter(actualFormParameters, expectedParameterValue);
    }
}

 private static void checkFormParameter(final List<String> actualFormParameters, final String expected) {
    assertNotNull(actualFormParameters);
    assertTrue(actualFormParameters.size() == 1);
    assertEquals(expected, actualFormParameters.get(0));
 }
}

所以基本上我想为此构建我的单元测试,类似于: 模拟执行器(以某种方式),根据请求正确生成JSON结果。 如果构建模拟对象层次结构,则可以将JSON结果编组回来。请纠正我,如果我错误的话。 我的问题在于,我需要为每个单独的请求创建层次结构,但我想这里没有任何花哨的东西可做。 我应该断言changeDeviceName方法使用预期的设备ID调用。
问题:
1. 独立于此测试反编组逻辑以确保所有其他方法按预期进行解组,这样做是否可以? 2. 有哪些适用的重构模式可以使代码更加简洁? 这两种方法太紧密耦合了。 3. 是否有任何更清洁的方法来代替这个测试用例? 我希望能得到更好的东西,我不喜欢这些参数捕获器,并且通常需要太多的代码来描述这种情况,而且会有许多类型的请求。
2个回答

0

我认为这个问题非常主观,我的答案也是基于我的观点:

  1. 我也会选择这种方式。就我个人而言,我在实现层或者服务器/客户端REST API迁移之后,经常遇到bug/问题。

  2. 存在一些重复代码 - RequestDescriptor, RequestCreator, ClientRequest, ClientResponse。所以你可以提取一些类来减少这些样板代码。看一下Square的Retrofit示例 https://github.com/square/retrofit/blob/master/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java

  3. 有人可能会说,集成测试更有价值,可以用来替代REST的单元测试。但我不这么认为。是的,我们在这里暴露了许多实现的细节,这意味着每次更改都必须同时更改测试。

我可能还会将请求转换为URL和请求体。并使用字符串或JSON比较进行断言。这样代码会更少。


0

对于集成测试和测试部署在服务器上的resteasy服务,restassured 是一个不错的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接