如何为调用 API 的方法编写 JUnit 测试?
Posted
技术标签:
【中文标题】如何为调用 API 的方法编写 JUnit 测试?【英文标题】:How to write JUnit test for a method that calls API? 【发布时间】:2019-11-01 12:03:24 【问题描述】:我必须为调用 API 然后处理响应的类编写测试。该类有两个公共函数和一个私有函数。第一个公共方法获取 ID 列表。第二个公共方法在每个 ID 的循环中调用,以获取与 ID 关联的详细信息。私有方法在第二个公共方法内部调用,因为基于 id 获取详细信息的调用是异步进行的。
我是 JUnits 的新手,虽然我知道我不应该测试 API 调用,只测试我的函数,但我仍然不明白单元测试应该断言什么。
以下是我的功能:
public List<Integer> fetchVehicleIds(String datasetId) throws ApiException
VehiclesApi vehiclesApi = new VehiclesApi();
List<Integer> vehicleIds;
vehicleIds = vehiclesApi.vehiclesGetIds(datasetId).getVehicleIds();
return vehicleIds;
public List<VehicleResponse> fetchVehicleDetails(String datasetId, List<Integer> vehicleIds) throws InterruptedException, ApiException
CountDownLatch latch = new CountDownLatch(vehicleIds.size());
List<VehicleResponse> vehiclesList = new ArrayList<>();
for (Integer vehicleId: vehicleIds)
populateEachVehicleDetail(datasetId, vehicleId, vehiclesList, latch);
latch.await();
return vehiclesList;
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException
ApiCallback<VehicleResponse> vehicleResponseApiCallback = new ApiCallback<VehicleResponse>()
@Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders)
vehiclesList.add(result);
latch.countDown();
;
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
根据我到目前为止所做的研究,我认为我必须使用 mockito 来模拟 API 调用?我仍然不清楚如何对功能进行单元测试。
【问题讨论】:
基本上,您希望通过将其与您控制的 Mock 交互的所有内容与实际 API 隔离开来。因此,创建一个 Mock VehiclesAPI,注入它(通过一个不在这里的设置器)并使用您自己制作的模拟响应处理对它的所有调用。 【参考方案1】:这两个语句确实是您要在单元测试中隔离的内容:
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException
....
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
...
1) 让你的依赖可模拟
但是你只能模拟你可以从类的客户端设置的东西。 这里的 API 是一个局部变量。因此,您应该更改您的类以公开依赖项,例如在构造函数中。 通过这种方式,您可以轻松地模拟它。
2) 让你的模拟不返回结果,而是调用回调。
在同步调用上下文中,您希望模拟返回的结果。
在带有回调的异步调用上下文中,情况有所不同。实际上,回调不会返回给调用者,但会调用回调以提供调用的结果。因此,您想要的是模拟 API 使用代表单元测试数据集的模拟参数调用 onSuccess()
回调:
@Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders)
vehiclesList.add(result);
latch.countDown();
在您的单元测试中,您应该以这种方式模拟每个预期调用的回调:
@Mock
VehiclesApi vehiclesApiMock;
// ...
// when the api method is invoked with the expected dataSetId and vehicleId
Mockito.when(vehiclesApiMock.vehiclesGetVehicleAsync(Mockito.eq(datasetId), Mockito.eq(vehicleId),
Mockito.any(ApiCallback.class)))
// I want to invoke the callback with the mocked data
.then(invocationOnMock ->
ApiCallback<VehicleResponse> callback = invocationOnMock.getArgument(2);
callback.onSuccess(mockedVehicleResponse, mockedStatusCode,
mockedResponseHeaders);
return null; // it is a void method. So no value to return in T then(...).
);
我认为ApiCallback
缺少演员表,但您应该有整体想法。
【讨论】:
【参考方案2】:你是对的:既然你想测试你的单元(即呈现的代码),你应该模拟 API(主要是:vehicleApi
实例)。
目前,没有办法在您的代码中注入 VehicleApi
的模拟实例(嗯,有,但它会涉及使用反射......我们不要走这条路)。您可以应用Inversion of Control 使您的代码可测试:与其在对象中构造VehicleApi
,不如编写一个期望VehicleApi
-instance 的构造函数:
public class YourClass
private final VehicleApi vehicleApi;
public YourClass(final VehicleApi vehicleApi)
this.vehicleApi = vehicleApi;
[...]
你赢了什么?好了,现在您可以将模拟对象注入到您的测试单元中:
@RunWith(MockitoJRunner.class)
public class YourClassTest
private final VehicleApi vehicleApiMock = mock(VehicleApi.class);
private final YourClass underTest = new YourClass(vehicleApiMock);
@Test
void someTest()
// GIVEN
[wire up your mock if necessary]
// WHEN
[write the test-call]
// THEN
[verify that the unit under test is in the expected state]
此示例假定 JUnit5 作为测试框架,Mockito 作为模拟框架,但也有其他选项。
测试写在Gherkin language:
- GIVEN
块描述了先决条件,即被测单元和外部(模拟)系统所在的位置
- WHEN
块执行应该被测试的动作
- THEN
块验证被测单元是否处于预期状态。
【讨论】:
以上是关于如何为调用 API 的方法编写 JUnit 测试?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 Spring 托管 bean 编写 Junit 测试用例?
如何为 DirectoryStream 编写 junit 测试