Как вы тестируете функцию, единственной целью которой является запрос внешнего API, но API использует сложный синтаксис запроса?

16

Единственная реальная логика в синтаксисе запроса для внешнего API. Я не хочу проверять, запрашивает ли он API, я хочу проверить, что он запрашивает его таким образом, что будут возвращены правильные данные. Например, некоторый псевдокод:

function retrieve_related_data(id)
{
  query = "[potentially long, syntactically complex query that
            uses param id to get some data]";
  results = api_wrapper.query(query);
  return results;
}

Более конкретный пример с составленным API:

function retrieveLifeSupportingObjectsWithinRegion(id)
{
  query = "
    within region(" + id + ") as r
    find objects matching hydration>0 and temp_range has 75
    send name, id, relative(position, r)        
  ";
  results = astronomicalObjectApiWrapper.query(query);
  return results;
}

The query is in a syntax custom to the API and is complex and there are multiple ways to achieve the same or similar results. The purpose of the function is not to get data identified by id but to find a subset of other data based on a fuzzy relationship to the data identified by id that also meets a few other requirements. The other requirements are always the same regardless of id but may change over time as the system is modified. For example, if the example api added support for gravity information, we may want to change the query to also use gravity to refine the results. Or maybe we come up with a more efficient way to check the temp range, but it doesn't change the results.

Что я хочу проверить, так это для данного ввода id возвращается правильный набор данных. Я хочу проверить это так, чтобы, если кто-то испортил запрос таким образом, что он больше не возвращает правильные данные, основываясь на idтом, что он потерпит неудачу, я также хочу, чтобы люди могли изменять запрос, чтобы уточнить его без необходимости также изменять тест.

Варианты, которые я рассмотрел:

  1. Я мог бы заглушить API, но это было бы слишком просто (проверьте, idприсутствует ли в запросе, а затем верните ожидаемый набор данных, если он есть, или неожиданный набор, если нет), слишком хрупким (проверьте, что строка запроса то, что находится в функции), или слишком сложный (убедитесь, что используемый запрос синтаксически правильный и приведет к возвращению правильных данных).

  2. Я мог бы отправить запрос к реальному API, но ожидаемые результаты могли со временем меняться по мере изменения данных во внешней системе вне контроля тестовой системы.

  3. Я мог бы взглянуть на настройку тестовой установки реального API для управления имеющимися у него данными, но это много усилий.

Я склоняюсь к # 2 и превращаю это в интеграционный тест, который не запускается часто, и наблюдаю, как часто изменения в данных внешней системы вызывают сбой теста. Я думаю, что сейчас это будет проще всего, но мне интересно, есть ли альтернативы, о которых я не думаю, или более эффективные способы решения этой проблемы. Любой совет будет принят во внимание.

Джошуа Коади
источник
Я думал об этом как о модульном тесте, но, может быть, это действительно интеграционный тест или приемочный тест низкого уровня?
Джошуа
2
Это API только для чтения? Или вы можете записать данные, с которыми вы можете надежно проверить свои показания ?
svidgen
Этот вопрос отличается от «как проверить, что мой sql (= сложный синтаксис) возвращает корректные данные»? С базами данных у вас обычно есть несколько интеграционных тестов, которые тестируют синтаксис crud-sql и фиктивное хранилище репозитория для проверки businesslogic
k3b

Ответы:

7

Может показаться, что при проверке ответа внешнего API мы будем тестировать нашу функцию, но это не совсем верно. Так или иначе, мы будем тестировать внешний API и среду, в которой работает API.

Наши тесты должны быть адресованы, чтобы гарантировать ожидаемое поведение написанного нами кода, а не написанного третьими лицами.

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

Почему я так говорю?

Я хочу проверить, что для заданного входного идентификатора возвращается правильный набор данных

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

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

Я хочу проверить это так, чтобы, если кто-то испортит запрос, чтобы он больше не возвращал правильные данные на основе идентификатора, он потерпит неудачу

Что если запрос в порядке, но данные неверны из-за ошибок в API? Не только данные находятся вне нашего контроля. Логика тоже.

Реализация функциональных тестов или сквозных тестов может помочь здесь. Вы можете обратиться к этому тесту, чтобы проверить определенные пути выполнения, чтобы, если API возвращал неверные данные, это, вероятно, вызывало неожиданное поведение и выходные данные. С другой стороны, я ожидал бы, что API выдаст ошибки, если мои запросы будут плохо отформатированы.

Но я также хочу, чтобы люди могли изменять запрос, чтобы уточнить его без необходимости изменять тест.

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

  • Класс, который запускается как тест, но не относится к плану тестирования
  • Сценарий оболочки + curl

Или что-то более сложное. Например, автономный клиент.

В любом случае функция, о которой идет речь, стоит двух видов тестов:

  • Модульный тест. Как вы сказали, вы должны заглушить внешний API, но это и есть цель модульных тестов. Тестирование нашего кода изолирует зависимости.

  • Интеграционный тест. Убедитесь, что код не только отправляет правильный запрос, но и правильно обрабатывает содержимое ответа, ошибки, перенаправления и т. Д. Выполните тесты для всех этих случаев, но не проверяйте данные .

Примечание: ваш вопрос похож на - как вы проверяете операторы SQL приложения?

Смежные вопросы :


1: Вас может заинтересовать ответ @ DocBrown на эту тему

LAIV
источник
«Проблема (IMO) в том, что вы слишком сосредоточены на тестировании внешнего API». - Я не вижу ничего, указывающего на то, что заявитель заинтересован в тестировании внешнего API вообще. Кроме того, вы говорите «заглушить внешний API», но есть ли у вас какие-либо предложения относительно того, должен ли запрашивающий пользователь использовать опцию «слишком простой», опцию «слишком хрупкий», опцию «слишком сложный» или какой-то четвертый вариант?
Таннер Светт
OP-вопрос - как проверить функцию, которая вызывает внешний API. Но, читая его сомнения, мне кажется, что он уделяет слишком много внимания проверке запроса и его результатов. Я сделал 4 предложения: (1) не делать тест API. (2) не используйте интеграционные тесты в качестве рабочей среды для настройки запроса. Сделайте инструмент вместо этого. (3) вернуться к основному вопросу, сделать его унитарный и интеграционный тест. Но не проверять содержание ответа API. (4) Спросите у менеджера проекта, должны ли они / могут ли сделать набор тестов внешнего API как часть плана тестирования проекта.
Laiv
1
Сам запрос я рассматриваю как «написанный нами код». Конечной целью является автоматическое тестирование, чтобы предупредить нас, если мы добавили ошибку в наш код. Принимая это к тому, что вы сказали об операторах SQL, я думаю, что это похоже на это - я думаю, мой вопрос похож на то, как вы проверяете, что ваш код запрашивает внешний API таким образом, что API будет реагировать так, как задумано (предполагая, что номинальный ответ). Я думаю, что вы говорите, чтобы исключить это из модульных и интеграционных тестов, но если запросы являются критически важными, мы могли бы отдельно настроить некоторые другие автоматизированные тесты для тестирования живого внешнего API.
Джошуа
1
IMO, лучший способ - выполнить функциональное тестирование, изменение предложения where, которое никогда не будет верным, вызовет другое поведение в одном или нескольких моих функциональных тестах. UT - только маленький кусочек в плане тестирования.
Laiv
2
Спасибо за то, что вы звучите В конечном счете, я предполагаю, что даже если в запросе есть собственная логика, он находится вне области модульного тестирования, поскольку запрос - это код, который «выполняется» вне тестируемой системы. Мне просто нужно было, чтобы кто-то сказал мне это несколько раз по-разному, прежде чем я увидел это;)
Joshua Coady
2

Я видел проверки модулей, которые проверяют, что сгенерированная строка запроса соответствует ожидаемому значению.

Тем не мение. Это было на мой взгляд, если ограниченное использование. Синтаксис запроса был сложным, возможно, с ошибками, поэтому у A были бесконечные возможности для проверки и B, даже если строка была «правильно» сгенерирована, неожиданные результаты могли быть возвращены в реальной среде.

Я думаю, что вы правы, выбрав вариант 2. Запустите интеграционные тесты на живом экземпляре.

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

Вариант 3 «развернуть тестовый экземпляр с фиктивными данными» лучше. Но это не влияет на ваши тестовые записи, так как вы можете направить те же тесты на тестовый сервер, если и когда это станет хорошим использованием времени для его развертывания.

Ewan
источник
0

Это зависит от API, но, если возможно, перейдите к варианту № 3 (частный экземпляр тестирования).

Заглушка API (вариант № 1) является наихудшим вариантом из-за упомянутых вами причин, и этот путь, вероятно, принесет больше вреда, чем пользы (много потерянного времени).

Запуск в режиме реального API (опция № 2) делает тесты нестабильными и ненадежными, и после нескольких ложных срабатываний люди просто перестанут их использовать. Меняются не только данные, но и сервис может быть недоступен. На мой взгляд, это сродни отсутствию тестов для запросов и полагаться на интеграционные / системные тесты для поиска проблем. Тем не менее, если данные API редко изменяются, а сам API почти всегда работает, то это может быть приемлемым вариантом. Большинство API не соответствуют этому описанию.

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

Михал Тененберг
источник
Итак, в основном, вы говорите, что модульные тесты (# 1) и интеграционные тесты (# 2) вредны? Хотя № 3 может показаться лучшим среди них, он может быть и самым дорогим. Он должен поддерживаться каждый раз, когда изменяется API. Без # 2 вы не будете знать о возможных ошибках в реальном API, пока ваше приложение не будет запущено (слишком поздно для принятия мер). Хорошо, # 1 кажется ненужным, потому что нет строк кода для тестирования ... сегодня ... Завтра, как знает ...
Laiv
Я говорю, что плохие тесты вредны, безусловно. Слабые тесты тратят много времени и сил и заставляют людей терять веру в юнит-тесты в целом. Тесты, которые ломаются с изменениями реализации (# 1) или просто случайно, когда изменения данных (# 2) не являются хорошими тестами.
Михал Тененберг
Интеграционный тест не проверяет данные. Вот и все. Они не могут прервать тестирование, просто подтвердите интеграцию. Тестирование - это не вопрос веры, а наличие хороших привычек, которые повышают ценность приложения
Laiv