Как я могу провести модульное тестирование класса, который требует вызова веб-службы?


21

Я пытаюсь протестировать класс, который вызывает некоторые веб-сервисы Hadoop. Код в значительной степени имеет вид:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

например, есть метод создания каталога, метод создания папки и т. д.

Учитывая, что код имеет дело с внешним веб-сервисом, который я не могу контролировать, как я могу выполнить это модульное тестирование? Я мог бы попытаться издеваться над клиентом / ответами веб-службы, но это нарушает принцип, который я часто видел в последнее время: «Не издевайтесь над объектами, которые вам не принадлежат». Я мог бы настроить фиктивную реализацию веб-службы - это все еще будет «модульный тест» или это будет интеграционный тест? Разве нельзя провести модульное тестирование на этом низком уровне - как бы это сделал практикующий врач TDD?


5
Где вы видели руководство о том, чтобы не издеваться над вещами, которые вам не принадлежат? Похоже, это огромная причина, почему вы должны насмехаться над вещами ...
Томас Оуэнс

1
Я читал его во многих блогах, один из которых ссылается на amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/…, который, как я знаю, является уважаемой книгой ( blog.8thlight. com / eric-smith / 2011/10/27 / thats-not-yours.html , mockobjects.com/2007/04/test-smell-everything-is-mocked.html )
Крис Купер,

1
@ChrisCooper: Могу ли я указать, что последняя ссылка очень устарела (с 2007 года). С тех пор многое изменилось. Из поста я чувствую, что издеваться было намного сложнее, чем тогда, когда вам нужно было на самом деле реализовать поведение, а не просто использовать платформу
имитации

Ответы:


41

На мой взгляд, вы должны высмеивать вызовы веб-службы, если это модульный тест, а не интеграционный тест.

Ваш модульный тест не должен проверять, работает ли внешний веб-сервис или правильна ваша интеграция с ним. Не слишком догматично относясь к TDD, обратите внимание, что побочным эффектом превращения вашего модульного теста в интеграционный тест является то, что он может работать медленнее, и вам нужны быстрые модульные тесты.

Кроме того, если веб-служба временно недоступна или работает неправильно, это может привести к сбою модульного теста? Это не кажется правильным. Ваш модульный тест должен завершиться неудачей только по одной причине: если в коде этого «модуля» есть ошибка.

Единственная часть кода, которая здесь уместна, это ...do something with response.... Дразнить остальных.


2
Помните, что вам придется поддерживать подпись фиктивного объекта и возвращать значения синхронно с сигнатурами, генерируемыми веб-сервисом Hadoop на протяжении неизбежных изменений в этом сервисе.
pcurry

Это редко стоит тестировать готовые компоненты, такие как Hadoop. Но если вы вызываете пользовательский веб-сервис, предоставленный другой группой или организацией, вы можете написать тест для самозащиты. Таким образом, когда что-то идет не так, вы можете быстро проверить, является ли проблема вашим кодом или веб-службой. Это не модульный тест для автоматического запуска; это диагностика для запуска по мере необходимости.
Кевин Клайн

@kevincline Я полностью согласен с необходимостью тестов, которые вы предлагаете, и действительно я пишу их в своей повседневной работе и доказал свою полезность. Но они по определению НЕ являются модульными тестами, о чем и был вопрос :) Подумайте над этим: если это модульный тест, и код не выполняется из-за изменения веб-службы, что такое «модуль», который вы тестируете? Что именно не удалось? Вы не проводите изолированное тестирование, как требуется модульным тестированием.
Андрес Ф.

1
@ AndresF .: Я думаю, что мы находимся в насильственном соглашении: «Эта [диагностика] НЕ является модульным тестом ...»
Кевин Клайн

@kevincline Правильно! Я неправильно понял ваш комментарий, извините!
Андрес Ф.

5

Я не согласен с «не издевайтесь над объектами, которые вам не принадлежат», когда вы проводите модульное тестирование.

Ложная цель существования заключается в том, что появятся модули, библиотеки, классы, которыми мы не будем владеть.

Мое предложение для вашего сценария - издеваться над вызовом веб-службы.

Настройте макет таким образом, чтобы он возвращал данные обратно в ваш модуль.
Убедитесь, что вы охватили весь сценарий, например, когда возвращаемые данные равны нулю, когда возвращенные данные верны и т. Д.

А для кода, которым вы владеете, ваша ответственность как разработчика заключается в том, чтобы гарантировать, что код, который вы создаете, работает должным образом во всех сценариях.


1

Я бы использовал что-то вроде EasyMock для этого теста. Фреймворки Mocking - это идеальный способ удаления внешних зависимостей класса, который дает вам полный контроль над результатами внешних зависимостей во время тестов. Чтобы немного расширить ваш пример:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

Первое, что вам нужно сделать, это извлечь логику в вашем классе, где вы используете Jersey, чтобы получить WebResource и вызвать веб-сервис в отдельный класс. Создание интерфейса для этого класса позволит вам создать макет, которому вы затем сможете диктовать поведение.

После создания этого интерфейса вы можете создать макет с помощью EasyMock, который вернет указанный объект в соответствии с вашим тестовым примером. Приведенный выше пример является упрощением того, как структурировать базовый макетированный тест и как будет работать ваш интерфейс.

Для получения дополнительной информации о фреймворках, пожалуйста, смотрите этот вопрос . Кроме того, в этом примере предполагается использование Java, но среды разработки доступны на всех языках, и, хотя они реализованы по-разному, в целом они будут работать одинаково


1

В этом случае допустимы насмешки, но они вам не нужны. Вместо модульного тестирования method(), вместо модульного тестирования только часть, которая обрабатывает ответ.

Извлеките функцию, которая принимает ResponseData(любого подходящего типа) и затем выполняет действие.

Вместо насмешек, теперь вы просто создаете объект ResponseData и передаете его.

Вы можете оставить вызов службы на полное интеграционное тестирование, которое охватит method()всего


0

Что я сделал, и это работает:

  1. Иметь весь код вызова веб-сервисов через прокси.
  2. Прокси-сервер вызывает класс, который статически знает, используем ли мы прокси-сервер или нет, и перенаправляет соответственно. Mocks - это просто HashMaps, которые для каждого запроса возвращают данный ответ.
  3. Запустите тесты несколько раз в следующем порядке:

3.1 Сначала все веб-сервисы тестируются. С каждой машины, даже машины разработчика. Это настоящие веб-сервисы, но работающие в среде разработки. Это означает, что веб-сервисы никогда не могут выйти из строя или ответить ошибочными значениями, потому что в противном случае каждый разработчик жалуется, что не может скомпилировать.

3.2 Затем запускаются все модульные тесты, внутренние для приложения. Это означает, что все веб-сервисы проверяются и тестируются, выполняя те же тесты, что и 3.1 (и они должны проходить тоже, в противном случае ложные проверки неверны), и вызываются реальным приложением, как если бы они действительно использовались. Если макеты ошибочны, вы можете запустить тест в 3.1 и записать эти значения (запрос, ответ) в HashMap.

3.3 Затем запускаются те же тесты, что и в 3.2, но на этот раз с реальными веб-сервисами, работающими в среде разработки.

После того, как все это будет выполнено, для реальной производственной среды вам просто нужно предоставить реальный адрес для каждого веб-сервиса. Надеюсь, это не потребует слишком больших изменений в конфигурации.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.