Передовой опыт: автоматизированное тестирование веб-API

Я написал программу на Python, которая работает с двумя разными API для получения данных из двух разных служб (CKAN и MediaWiki). В частности, есть класс Resource, который запрашивает данные у вышеперечисленных сервисов и обрабатывает их.

В какой-то момент я пришел к выводу, что для моего приложения нужны тесты. И проблема в том, что все примеры, которые я нашел в Интернете и в книгах, не касаются таких случаев.

Например, внутри класса Resource у меня есть метод:

def load_from_ckan(self):
    """
        Get the resource
        specified by self.id
        from config.ckan_api_url 
    """
    data = json.dumps({'id': self.id})
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    url = config.ckan_api_url + '/action/resource_show'
    r = requests.post(url, timeout=config.ckan_request_timeout, data=data, headers=headers)
    assert r.ok, r
    resource = json.loads(r.content)
    resource = resource["result"]
    for key in resource:
        setattr(self, key, resource[key])

Метод load_from_ckan получает данные о ресурсе из CKAN API и присваивает их объекту. Это просто, но...

Мой вопрос: как протестировать подобные методы? ИЛИ Что мне следует протестировать здесь?

Я подумал о возможности консервировать (сохранять) результаты на HDD. Затем я мог загрузить его в тест и сравнить с объектом, инициализированным функцией load_from_ckan(). Но CKAN — это платформа, управляемая сообществом, и такое поведение таких тестов будет непредсказуемым.

Если существуют какие-либо книги по философии автоматизированных тестов (например, что тестировать, что не тестировать, как сделать тесты осмысленными и т. д.), пожалуйста, дайте мне ссылку на них.


person Ivan Ermilov    schedule 06.02.2013    source источник


Ответы (3)


При любом тестировании ключевой вопрос заключается в том, что может пойти не так?

В вашем случае, похоже, три риска:

  • Рассматриваемый веб-API может перестать работать. Но вы это уже проверяете, с assert r.ok.
  • Вы или кто-то другой можете в будущем внести в код ошибочное изменение (например, ввести опечатку в переменной), что нарушит его работу.
  • API может измениться, и он больше не будет возвращать нужные вам поля или формат.

Кажется, вы могли бы написать довольно простой тест для последних двух, в зависимости от того, на какие данные из этого API вы действительно полагаетесь: например, если вы ожидаете, что JSON будет иметь поле с именем «температура», которое является плавающим. укажите число градусов Цельсия, вы можете написать тест, который вызывает вашу функцию, а затем проверяет, что self.temperature является экземпляром 'float' и находится в разумном диапазоне значений (от -30 до 50?). Это должно дать вам уверенность в том, что и API, и ваша функция работают должным образом.

person rkday    schedule 06.02.2013
comment
Проверка атрибутов возвращаемого объекта — отличная идея, спасибо! Я просто проверю, содержит ли объект все атрибуты, которые я буду использовать позже в своем коде. - person Ivan Ermilov; 07.02.2013

Как правило, если вы хотите протестировать какую-либо внешнюю службу, подобную этой, вам нужно будет использовать фиктивный / фиктивный объект для имитации API внешней службы. Это должно быть настраиваемым во время выполнения либо с помощью аргументов метода, либо с помощью конструктора класса, либо с помощью другого типа косвенного обращения. Другим более сложным вариантом может быть исправление глобальных переменных во время тестирования, например, «запросы на импорт; request.post = fake_post», но это может создать больше проблем.

Так, например, ваш метод может принимать такой аргумент:

def load_from_ckan(self, post=requests.post):
    # ...
    r = post(url, timeout=config.ckan_request_timeout, data=data,
        headers=headers)
    # ...

Затем во время тестирования вы должны написать свою собственную функцию публикации, которая возвращает результаты json, которые вы увидите, возвращаясь из ckan. Например:

 def mock_post(url, timeout=30, data='', headers=None):
     # ... probably check input arguments
     class DummyResponse:
         pass
     r = DummyResponse()
     r.ok = True
     r.content = json.dumps({'result': {'attr1': 1, 'attr2': 2}})
     return r

Конструирование результата в вашем тесте дает вам гораздо больше гибкости, чем травление результатов и их возврат, потому что вы можете сфабриковать условия ошибки или сосредоточиться на определенных форматах, которые ваш код может не ожидать, но вы знаете, что они могут существовать.

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

person Ian Wilson    schedule 07.02.2013

На этом этапе вы можете проверить правильность анализа ответа от CKAN. Таким образом, вы можете получить JSON из CKAN и убедиться, что он возвращает данные с интересующими вас атрибутами.

person prafulfillment    schedule 06.02.2013