Является ли хорошей идеей иметь отдельные методы тестирования для каждого шага?


10

Я тестирую REST API. Допустим, он возвращает структуру JSON. Каков наилучший подход к тестированию сервера? Каждый шаг теста может быть успешным, только если все предыдущие были успешными.

Структура A: проверить все сразу

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

Это, кажется, лучшее решение.

Преимущества:

  • Только один запрос к серверу
  • Я тестирую поведение в целом "Возвращает ли сервер JSON с ключом X?"

Структура B: постепенно добавляйте утверждения к каждому тесту

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

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

Структура C: сделать запрос один раз и запустить отдельные методы тестирования для кэшированного ответа

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

Преимущества:

  • Нет повторного (дорогого) запроса к серверу
  • По-прежнему имеет методы проверки одного утверждения

Какую тестовую структуру наиболее целесообразно использовать?


Пожалуйста, прекратите изменять свой вопрос впоследствии таким образом, который лишает законной силы существующие ответы! Спасибо.
Док Браун

Извините, что доставил вам неудобства, но вы бы предложили поступить иначе?
mrplow

Во-первых, подумайте дважды, если вам действительно нужно изменить свой вопрос таким образом. Если вы действительно думаете, что должны добавить что-то, что лишает законной силы некоторые ответы, вы можете сообщить всем авторам этих ответов, оставив комментарий под своим ответом, спрашивая их, хотят ли они что-то изменить или добавить в свой текст.
Док Браун

2
Я действительно предположил, что авторы ответов уведомляются, если вопрос изменен. Вот почему я не хотел спамить комментарии не по теме. Я буду уведомлять авторов в будущем. И спасибо за ответ на мой вопрос.
mrplow

Ответы:


3

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

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

Не следуй этому вслепую. Даже если каждый тест должен проверять одну вещь, вы все равно должны подумать о том, какой большой или маленькой должна быть каждая «вещь», и для этого вы должны помнить, почему вы хотите, чтобы каждый тест проверял одну вещь - чтобы убедиться, ошибка в первой вещи не оставляет вторую вещь непроверенной.

Итак, вы должны спросить себя: «Мне действительно нужна эта гарантия здесь?»

Допустим, в первом тесте есть ошибка - код ответа HTTP - нет 200. Итак, вы начинаете взламывать код, выяснять, почему вы не получили код ответа, который вы должны иметь, и устранять проблему. И что теперь?

  • Если вы снова запустите тестирование вручную, чтобы убедиться, что ваше исправление действительно решило проблему, вы должны столкнуться с любой другой проблемой, скрытой при первом сбое.
  • Если вы не запускаете его вручную (возможно, потому что это занимает слишком много времени?), А просто нажимаете на свое исправление, ожидая, пока сервер автоматических тестов запустит все, тогда вы можете поместить разные утверждения в разные тесты. Циклы в этом случае очень длинные, поэтому стоит приложить усилия, чтобы обнаружить как можно больше ошибок в каждом цикле.

Есть еще несколько вещей, чтобы рассмотреть:

Утверждения зависимостей

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

Если у вас есть служба REST (или любой другой протокол HTTP), который возвращает ответы в формате JSON, вы обычно пишете простой клиентский класс, который позволяет вам использовать методы REST, такие как обычные методы, которые возвращают обычные объекты. Если предположить, что у клиента есть отдельные тесты, чтобы убедиться, что он работает, я бы отбросил первые 3 утверждения и оставил только 4!

Почему?

  • Первое утверждение является избыточным - класс клиента должен выдать исключение, если код ответа HTTP не равен 200.
  • Второе утверждение является избыточным - если ответ пустой, результирующий объект будет нулевым или каким-либо другим представлением пустого объекта, и вам некуда будет вставлять ключ X.
  • Третье утверждение является избыточным - если JSON недействителен, вы получите исключение при попытке его проанализировать.

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

Как вы хотите получать отчеты?

Допустим, вы не получаете электронные письма с тестового сервера, но вместо этого отдел QA проводит тесты и уведомляет вас о неудачных тестах.

Джек из QA стучит в вашу дверь. Он говорит, что первый метод тестирования не удался, а метод REST вернул неверный код ответа. Вы благодарите его и начинаете искать основную причину.

Затем приходит Джен из QA и говорит, что третий метод тестирования не удался - метод REST не вернул допустимый JSON в теле ответа. Вы говорите ей, что вы уже просматриваете этот метод, и вы полагаете, что то же самое, что заставило его вернуть неверный код выхода, также заставило его вернуть что-то, что не является допустимым JSON и больше похоже на трассировку стека исключений.

Вы возвращаетесь к работе, но затем приходит Джим из QA, говоря, что четвертый метод теста не удался, и в ответе нет ключа X ...

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

Электронные письма с тестового сервера легче отклонить, но все же - не лучше ли вам просто ОДНАЖДЫ раз сообщать , что что-то не так с методом теста, и самостоятельно просматривать соответствующие журналы тестов?


3

Если вы можете с уверенностью предположить, что выполнение запроса к серверу с одинаковыми параметрами будет вести себя всегда одинаково, метод B практически бессмысленен - ​​зачем вам четыре раза вызывать один и тот же метод для получения одних и тех же данных ответа четыре раза, когда достаточно одного вызова?

И если вы не можете с уверенностью предположить это и захотите включить его в тест, вам лучше будет выполнить тест А несколько раз.

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

Альтернатива C, кажется, объединяет A с одним преимуществом, которое я упомянул выше для B. Если ваша инфраструктура тестирования позволяет легко и просто структурировать ваш код без больших накладных расходов над B, это выполнимый подход. Тем не менее, это добавляет некоторую дополнительную сложность к A, поэтому я бы использовал его, только если я когда-нибудь захочу включить и выключить отдельные тесты, в противном случае примените принцип YAGNI и придерживайтесь самого простого решения (A).

TLDR: начните с A, если вы уверены, что всегда хотите, чтобы все утверждения выполнялись в одном тесте, измените рефакторинг на C, если вы заметили, что вам нужно иметь более легкий контроль со стороны для отдельных утверждений.


0

Как и любой код, избегайте преждевременной оптимизации. Сначала напишите свои тесты, чтобы их было легко читать и поддерживать. Когда тесты станут слишком медленными, оптимизируйте их. В вашем довольно простом примере и A, и B будут легко читаться и обслуживаться, поэтому выбирайте тот, который вам нужен, пока все не станет слишком медленным (структура B) или слишком сложным (структура A).

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

Если ваш сервер заполнен, и вам нужно сделать несколько медленных вызовов API, чтобы привести сервер в состояние для теста, тогда вы должны использовать другой подход, иначе тесты могут занять несколько минут. Например, вы можете запустить обновление базы данных, чтобы внедрить данные в тестовую базу данных, чтобы вы могли быстро получить объект в подходящем состоянии для тестирования. Тест быстрый и читаемый, но сложнее в обслуживании. В качестве альтернативы вы можете написать фасад перед API, чтобы несколько вызовов API стали едиными вызовами API, которые более точно соответствуют бизнес-процессу, который вы тестируете.


0

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


При написании любого кода (или, возможно, даже при создании чего-либо еще), всегда спрашивайте себя: «Почему существует такая практика?»
Почему мы говорим, что должны быть разные тесты для всего?

Есть два случая, когда это нужно:

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

Есть две причины, по которым вы сталкиваетесь с этими случаями:

  1. «каждый шаг теста может быть успешным только в том случае, если все предыдущие были успешными» действительно не применимо к вашей функции продукта
  2. у вас недостаточно знаний о продукте из-за нехватки опыта или времени, или из-за чрезмерной сложности продукта

Если вы по какой - то причине не может объявить по крайней мере , одной из этих причин , чтобы иметь место только слепо взять структуру B .


В противном случае (я надеюсь , что вы здесь) вы выбираете A .


Также вы можете задать этот вопрос на сайте Stackexchange по обеспечению качества и тестированию программного обеспечения .

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