我有一个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
方法。
问题:
是否可以独立测试unmarshal逻辑,以确保所有其他方法按预期进行解组,或者有更好的方法吗?
我是否可以使用任何适用的重构模式来使其更清洁?这两种方法过于紧密耦合。
是否有更清洁的方法而不是此测试用例。我希望我能得到更好的东西,我不喜欢这些论证的俘获者,并且通常需要太多的代码来简单描述这种情况,并且会有很多类型的请求。
答案 0 :(得分:0)
我认为这个问题很主观,我的回答也是IMO:
我也会这样。至于我,我看到很多次在实现级别或服务器/客户端REST API迁移后发现错误/问题
有一些重复 - 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
有人可以说,集成测试更有价值,不能用作添加,而是用于REST的单元测试。但我不这么认为。是的,我们在这里公开了很多关于实施的细节,这意味着你必须改变测试的每一个改变
我可能还会将请求转换为url和body。并对断言使用字符串或JSON比较。这将是更少的代码
答案 1 :(得分:0)
对于集成测试和测试服务器restassured中部署的resteasy服务是好的。