Как определить, был ли автоматический функциональный тест успешным

#testing #tdd #functional-testing

Вопрос:

Цель:

  • Определите, был ли функциональный тест успешным.

Сценарий:

  • У нас есть функциональное требование: «Пользователь должен иметь возможность зарегистрироваться под именем пользователя и паролем. Имя пользователя должно быть действительным адресом электронной почты. Пароль должен содержать не менее 8 символов».
  • У нас есть метод «SignupResult UserManager.Регистрация(строковое имя пользователя, строковый пароль)».
  • Нам нужен счастливый тест с допустимыми входными данными и грустный тест с недопустимыми входными данными.
  • Подсистемы пользовательского менеджера (например, база данных) могут быть как поддельными, так и реальными системами.

Вопрос: Как лучше всего определить, был ли пользователь успешно зарегистрирован? Я могу представить себе следующие варианты:

  • Если над какой-либо подсистемой издевались, можно было проверить, вызывалась ли конкретная функция, такая как «DB.saveUser (…)». Это разрушило бы идею о том, что функциональный тест является тестом черного ящика, и требует, чтобы автор тестов знал о реализации.
  • Если мы используем реальные подсистемы, можно, например, проверить, существует ли строка в базе данных. Это было бы неадекватно, как и попытка выше.
  • Можно было бы использовать другую функцию, например «UserManager.CheckUser (…)», чтобы проверить, был ли создан пользователь. Это привело бы к введению другого метода, который тестируется, также могут быть операции, у которых не было бы «аналога теста», или их пришлось бы реализовать только для тестирования-это кажется не идеальным.
  • Мы могли бы проверить результат «Результат регистрации» и/или проверить наличие исключений. Для этого потребуется определить интерфейс метода. Это также потребует, чтобы все методы возвращали разумное значение — я думаю, что в любом случае это будет хороший подход.

Мне кажется, что последние методы-это правильный путь. Я прав? Существуют ли другие подходы? Как бы мы проверили побочные эффекты, такие как «новому пользователю было отправлено электронное письмо» ?

Ответ №1:

Возможно, вам захочется ознакомиться с концепцией Тестовой пирамиды.

Не существует единственно правильного способа разработки и внедрения автоматизированных тестов — только компромиссы.

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

Проблема в том, что автоматические тесты, как правило, оставляют за собой след постоянных изменений состояния. Например, однажды я сделал что-то вроде того, о чем вы спрашиваете, и написал серию автоматических тестов, в которых использовалась реальная система (API REST) для регистрации новых пользователей.

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

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

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

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

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

Комментарии:

1. Таким образом, вы предпочли бы проверку (с помощью макета/подделки) через общедоступный API, чтобы проверить, соответствует ли тестируемый код требуемому поведению?

2. @MichaelRall Как описано в связанных статьях, я бы написал тесты, которые взаимодействуют и проверяют через общедоступный интерфейс системы, но с базой данных и другими реальными системными зависимостями, замененными подделками (а не насмешками).

Ответ №2:

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

Итак, подумайте о том, почему именно пользователи должны регистрироваться? Просто чтобы увидеть реакцию? Как насчет требования:

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

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

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

Комментарии:

1. Эй, хорошие моменты, для меня все еще трудно получить функцию/требование, а затем перейти к тесту, который действительно определяет, чего следует достичь. Я часто делаю шаг дальше, потому что «мы решили эту проблему 10 раз так и так». Цель состоит в том, чтобы сосредоточиться на том, чего необходимо достичь, пока это действительно не свелось к минимуму. Чтобы взять мой пример, у меня были бы другие требования, такие как «пользователь должен подтвердить по электронной почте», но отправной точкой всегда является «пользователь должен войти в систему, чтобы использовать систему», а затем мы идем дальше.