如何正确单元测试RestEasy响应?

时间:2014-01-10 22:38:00

标签: java json unit-testing mockito resteasy

我有一个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));
 }
}

所以基本上我想建立我的单元测试,例如:

模拟executor(以某种方式)根据请求正确生成JSON结果。 如果我错了,请纠正我,但如果构建模拟对象层次结构,我可以编组JSON结果。 我的问题是我需要为每个请求创建层次结构,但我想我不能在这里做任何更好的事情。 我应断言使用预期的设备ID调用changeDeviceName方法。

问题:

  1. 是否可以独立测试unmarshal逻辑,以确保所有其他方法按预期进行解组,或者有更好的方法吗?

  2. 我是否可以使用任何适用的重构模式来使其更清洁?这两种方法过于紧密耦合。

  3. 是否有更清洁的方法而不是此测试用例。我希望我能得到更好的东西,我不喜欢这些论证的俘获者,并且通常需要太多的代码来简单描述这种情况,并且会有很多类型的请求。

2 个答案:

答案 0 :(得分:0)

我认为这个问题很主观,我的回答也是IMO:

  1. 我也会这样。至于我,我看到很多次在实现级别或服务器/客户端REST API迁移后发现错误/问题

  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的单元测试。但我不这么认为。是的,我们在这里公开了很多关于实施的细节,这意味着你必须改变测试的每一个改变

  4. 我可能还会将请求转换为url和body。并对断言使用字符串或JSON比较。这将是更少的代码

答案 1 :(得分:0)

对于集成测试和测试服务器restassured中部署的resteasy服务是好的。