我有一个RESTEasy客户端端点,连接到基于REST的数据库服务器,该服务器提供JSON响应。另一端的数据模型(使用JSON注释注释的POJO)可供使用。
我想正确地单元测试正在与请求/响应通信的方法。使用JUnit和Mockito编写测试代码。
如果一个方法发送请求,需要解析JSON结果,应该提取创建实体的新ID并“重用”此ID以使自动生成的设备名称更易读且更友好,则情况变得更加复杂。我想也许这个方法应该分成两个方法,但我看不到任何干净和良好的解决方案,因为这个方法将被用作系统中其他组件的访问点,因此一个方法应该完成一件事。
说了那么多,让我们看看代码本身:
所以基本上我想为此构建我的单元测试,类似于: 模拟执行器(以某种方式),根据请求正确生成JSON结果。 如果构建模拟对象层次结构,则可以将JSON结果编组回来。请纠正我,如果我错误的话。 我的问题在于,我需要为每个单独的请求创建层次结构,但我想这里没有任何花哨的东西可做。 我应该断言
问题:
1. 独立于此测试反编组逻辑以确保所有其他方法按预期进行解组,这样做是否可以? 2. 有哪些适用的重构模式可以使代码更加简洁? 这两种方法太紧密耦合了。 3. 是否有任何更清洁的方法来代替这个测试用例? 我希望能得到更好的东西,我不喜欢这些参数捕获器,并且通常需要太多的代码来描述这种情况,而且会有许多类型的请求。
我想正确地单元测试正在与请求/响应通信的方法。使用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. 是否有任何更清洁的方法来代替这个测试用例? 我希望能得到更好的东西,我不喜欢这些参数捕获器,并且通常需要太多的代码来描述这种情况,而且会有许多类型的请求。