Как написать тест JUnit для метода, который вызывает API? - PullRequest
1 голос
/ 18 июня 2019

Я должен написать тест для класса, который вызывает API, а затем обрабатывает ответ.Класс имеет две публичные функции и приватную функцию.Первый публичный метод извлекает список идентификаторов.Второй публичный метод вызывается в цикле для каждого идентификатора, чтобы получить детали, связанные с идентификатором.Закрытый метод вызывается внутри второго открытого метода, поскольку вызовы для извлечения деталей на основе идентификатора выполняются асинхронно.

Я новичок в 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);

}

Основываясь на исследовании, которое я провел до сих пор, я думаю, что мне нужно высмеивать вызовы API с помощью mockito?Мне все еще неясно, как функциональность может быть проверена модулем.

Ответы [ 2 ]

4 голосов
/ 18 июня 2019

Эти два утверждения действительно являются вещами, которые вы хотите выделить в своем модульном тесте:

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, но вы должны иметьобщая идея.

3 голосов
/ 18 июня 2019

Вы правы: поскольку вы хотите протестировать свой модуль (т.е. представленный код), вы должны смоделировать API (в основном: экземпляр vehicleApi).

Как есть сейчас, нет способа внедрить в ваш код макетированный экземпляр VehicleApi (ну, есть, но это предполагает использование отражения ... давайте не будем идти по этому пути). Чтобы сделать ваш код тестируемым, вы можете использовать Inversion of Control : вместо создания VehicleApi внутри вашего объекта, напишите конструктор, который ожидает VehicleApi -экземпляр:

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 в качестве основы для mocking, но есть и другие варианты.

Тест написан на корнишоне : - блок GIVEN описывает предварительные условия, т. е. в котором находятся тестируемое устройство и внешняя (имитируемая) система (и) - блок WHEN выполняет действие, которое должно быть проверено - блок THEN подтверждает, что тестируемое устройство находится в ожидаемом состоянии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...