Для начала
Если вы уже опытный тестировщик, создать хороший тестовый набор проще простого. Но если нет, то научиться правильно тестировать — что тестировать, что не тестировать, какие виды тестов должны быть в приоритете — не так-то просто.
Как правило тестирование «тяжелое и дорогое сначала». Все кажется новым и сложным, кейсы, которые вы пытаетесь реализовать — не работают, а вы не понимаете, почему не работают, это связано с неправильным кодом или это ваша ошибка, и т.д.
Все мы знаем, что такое пирамида тестирования и, как принято, подходим к ней снизу (то есть начинаем тестирования с нижних уровней):
Подход к пирамиде снизу имеет смысл. Начинать с юнит-тестов проще, потому что они всегда быстрые, не требуют сложных контекстов и инструментов. Юнит — что бы мы ни подразумевали под этим словом: функцию, компонент и т.д., содержит как правило лишь несколько строчек кода, и не очень много зависимостей (или не имеет их вовсе).
В чем самый большой недостаток такого подхода? По сути, в уверенности, или надежности. Тестирование в целом — это проверка надежности, и это всегда в какой-то мере компромисс между надежными, но медленными тестами, и не очень надежными, но быстрыми тестами.
Если вы новичок в тестировании, то контекст этого термина «надежности/уверенности» может быть не совсем понятен сейчас: настолько вы можете быть уверены, что приложение, которое вы тестируете сейчас, работает, если тесты прошли? Это и есть надежность в тестировании.
Почему модульные тесты дают мало уверенности, считаются не очень «уверенными», то есть не очень надежными? Примеры:
- Если функция
isValidEmail
прошла тесты, уверены ли вы, что форма регистрации в тестируемом фронтэнд-приложении работает? - Если компонент
Input
в React проходит тесты, уверены ли вы, что форма регистрации тоже работает? - Если весь компонент
RegisterForm
проходит тесты, уверены ли вы, что пользователь действительно может зарегистрироваться?
Ответ — нет. Приложение состоит из множества юнитов, интегрированных друг с другом, не считая некоторых «презентационных» (CSS) проблем, которые могут помешать пользователю зарегистрироваться, например из-за изображения с higherz-index, закрывающего кнопку отправки.
Еще раз о недостающем опыте тестирования у новичков (каким я сам был два года назад). Все новое требует большой когнитивной нагрузки, и нельзя одновременно понять и воспринять слишком много нового. Сложно заниматься обычной работой над приложением и одновременно новой задачей тестирования, переключаясь между миром юнит-тестов и UI-тестов. Эти две сферы требуют совсем разных инструментов и усилий.
Взгляните на этот прекрасный рисунок из проекта о лучших практиках тестирования в Node.js:
Эта многозадачность и это «переключение» представляет проблему даже для опытных разработчиков, а у новичков дела обстоят еще хуже.
Результаты применения подхода «снизу вверх»
Вы неизбежно уделите больше всего внимания основанию пирамиды — юнит-тестам. Куча тестов, которые вам будет нужно написать, позволят вам отлично войти в мир тестирования, но без особой уверенности в результатах тестирования. Вы можете обнаружить, что спрашиваете:
- В чем реальная польза большей части написанных мною юнит-тестов?
- Я потратил много времени на юнит-тесты, а приложение все равно падает, как и прежде, в чем дело?
- Честно говоря, сейчас я менее уверен в продукте, чем до начала тестирования
Проблема есть, но она не в вас, а в неправильном подходе.
Какое решение я предлагаю? Начинать сверху и фокусироваться на UI-тестах
Прежде всего, что такое UI-тест? (также называемый функциональным тестом, а наиболее правильно — сквозным, E2E-тестом). По сути, это сценарий, который открывает реальный браузер и взаимодействует с элементами DOM точно так же, как это делает реальный конечный пользователь. Вот видео, которые могут объяснить больше, чем тысяча слов: посмотрите на E2E-тест проекта Conduit — RealWorld и некоторые UI-тесты Conio Backoffice.
В приведенных выше видео можно видеть реальный браузер, который загружает все фронтенд-приложение и взаимодействует с ним.
Плюсы:
- Ваше приложение тестируется в том же контексте, что и конечный пользователь (браузер), что означает более высокую надежность. Даже если вы напишете всего один UI-тест, он даст вам больше уверенности, чем сотня юнит-тестов.
- Тестируемый в UI путь пользователя (стандартные действия пользователя, такие как регистрация, создание нового поста и т.д.) — это то же самое, что должен выполнить конечный пользователь, что означает меньшую когнитивную нагрузку на вас, и у вас останется больше времени, чтобы посмотреть на проект свежим глазом.
- Честно говоря, автоматизация браузера доставляет больше удовольствия, чем автоматизация терминала.
- UI-тестирование лучше подходит для большинства проектов, с которыми вы ежедневно работаете, по крайней мере от небольших до средних. От тестирования лендинга до тестирования маленькой CMS: все они требуют хотя бы нескольких UI-тестов и этого может быть достаточно, и при этом можно обойтись лишь несколькими юнит-тестами, исходя из общей уверенности в проекте и соблюдения сроков релиза. Но почти никто из нас не работает в Facebook, Spotify, Netflix и подобных корпорациях, требующих соблюдения строгих стратегий тестирования, требований к покрытию и т.д. В общем, если вы работаете в средней или крупной продуктовой компании, то, скорее всего, вам этот пост не очень полезен, потому что «классическое» тестирование — это основа культуры вашей компании.
У предлагаемого мною подхода есть и минусы, их я перечислю позже. Пока я предлагаю такой подход:
А может это плохая практика?
Этот пост не о лучших или худших практиках, а о том, как легко и выгодно для проекта привлечь фронтенд-разработчиков в мир UI-тестирования. Моя цель — предложить более практичный, работающий подход.
Может возникнуть вопрос:
Если UI-тестирование такое чудесное и решающее все проблемы, то почему до сих пор существуют другие виды тестирования? Хороший вопрос. Обратите внимание, что я не против юнит-тестирования! Каждый вид тестирования важен, и разные типы тестов дают разный фидбек. Но в целом разработчик или тестировщик в небольшом проекте, который подходит к тестовой пирамиде начиная с верха, то есть с UI-тестов, будет достаточно уверен в качестве.
Минусы
Далее посмотрим на минусы высокоуровневых UI-тестов:
- Они медленные: да, я знаю, по приведённых выше видео может показаться, что UI-тесты супербыстрые, но это конечно не так. Они быстрые, если у вас их пять, десять, двадцать, но когда их у вас сотни и на них уходят минуты (или больше), вы начинаете задумываться.
- Они дают в основном высокоуровневую обратную связь: если кнопка отправки формы не работает, в чем ошибка? Существует масса возможных причин, и стандартный UI-тест не позволяет определить многие из них.
- Они выводят на экран все приложение, и это может оказаться излишним, если нужно протестировать что-то небольшое. Некоторые сложные случаи (корнер-кейсы), которые необходимо протестировать, вообще не воспроизводятся.
Решение вышеперечисленных проблем (минусов), если они возникают, таково: спускаться вниз по пирамиде. И тогда вы дойдете до тестов более низкого уровня, и там определите причины дефектов.
Итак
Рассмотрим результат обоих подходов:
- Снизу вверх: вы сомневаетесь в полезности написанных вами сотен юнит-тестов и не вполне понимаете, как эти тесты могут помочь вам повысить уверенность в тестировании.
- Сверху вниз: у вас есть пусть несколько но надежных UI-тестов, и вы просто спускаетесь вниз по пирамиде. А если в этом нет необходимости, если особых проблем нет, значит, ваш проект небольшой и вовсе не нуждается в дополнительных тестах.