Тестирование Flutter-приложений: инструменты, преимущества, проблемы
Привет! Меня зовут Мария Лещинская, я QA-специалист в Surf. Наша компания разрабатывает нативные приложения с 2011 года, а с 2018-го мы занимаемся ещё и разработкой под Flutter.
В этом материале сравним возможности тестирования нативных и кроссплатформенных приложений. Я поделюсь впечатлениями от работы с Flutter и расскажу, какие инструменты мы в Surf используем при тестировании, чем Flutter удобен и с какими проблемами мы столкнулись.
Возможности тестирования во Flutter не уступают нативным
Когда в компании меняется подход к разработке или появляется новая технология, важно, чтобы не сильно страдали возможности тестирования. Идеально, если при работе с новым языком или фреймворком QA-специалисты продолжают пользоваться привычным стэком инструментов и технологий, зарекомендовавших себя наилучшим образом.
Тестируя нативные приложения, мы в Surf пользуемся автотестированием и чтением и подменой пакетов. Без автотестов сейчас никуда, особенно при регрессе, а без помощи прокси снижаются вариативность приложения и охват множества кейсов.
Нам было важно, чтобы привычные возможности оставались и при тестировании Flutter-приложений.
Автотестирование
При автотестировании нативных приложений Surf работает с фреймворком Calabash и Ruby. Когда появился Flutter, нам первым делом стало интересно: можно ли не использовать Calabash, но при этом в полной мере работать с автотестами так, как мы привыкли, — или даже круче.
Оказалось, что не просто можно —, а можно даже без сторонних сервисов: во Flutter интеграционные тесты и тестирование виджетов в консоли доступны из коробки. Подробнее об этом рассказал наш Flutter-разработчик в статье про автотестирование на Flutter.
Автотесты на Flutter одновременно и кроссплатформенные, и нативные: писать тесты можно внутри проекта приложения, при этом работать они будут на обеих платформах. Когда перед глазами проект целиком, можно дописать недостающие id-шники или даже найти баги и исправить их, — это ещё одна возможность повысить качество приложения.
Flutter также поддерживает Behavior Driven Development — BDD — подход. Мы используем его для UI-тестов. В качестве языка выбрали Gherkin: в нём можно использовать feature-файлы, писать сценарии на английском и русском языках. Он понятный, в нём есть реализация шагов сценариев без дополнительных аргументов внутри или с ними, возможность кастомизации запуска автотестов: например, прогон некоторых сценариев по тегам, а не всех написанных тестов целиком.
Чтобы использовать Gherkin при тестировании Flutter-приложений, подключили opensource-фреймворк flutter_gherkin.
Когда мы поняли, что автотесты на Flutter есть, нам стало интересно, какие отличия между технологиями Calabash и Dart+Gherkin, какой подход лучше. Давайте вместе сравним их.
1. Feature-файлы абсолютно идентичны при обоих подходах.
Например, сценарий авторизации по пин-коду будет корректно восприниматься и на Dart, и на Ruby с использованием Calabash:
Сценарий: Корректная авторизация в приложении с первого раза (короткий код)
Когда Я запускаю приложение
И Я вижу экран ввода короткого кода
Тогда Я ввожу корректный короткий код
И Я вижу главный экран
Обе технологии поддерживают русский, английский и другие языки.
2. Steps отличаются в реализации.
Нельзя сказать, что именно удобнее: структура внутри определённой технологии не меняется, и это хорошо.
3. Для конфигурации тестов и работы с ними Flutter использует дополнительный файл .dart. При работе с Calabash такого единственного файла не существует.
Сказать, что это недостаток внутри Flutter или Calabash, на наш взгляд, нельзя — это всего лишь специфика работы с конкретными инструментами и технологиями.
4. Для удобства работы с элементами в приложении необходимо, чтобы у каждого элемента был свой id. При работе с автотестами с Calabash нужно заранее позаботиться о том, чтобы на обеих платформах в тестируемых приложениях были id. На Dart можно добавлять id в процессе написания автотестов, не перезаливая отдельно файлы iOS и Android приложений — это удобно и экономит время.
Наш вывод: автотесты на Dart совсем не уступают автотестированию с помощью фреймворка Calabash.
Прокси
Для повышения покрытия приложения кейсами в Surf используют программы для чтения и подмены трафика — например, Charles. Анализ клиент-серверного взаимодействия позволяет:
- Определять, есть ли реальное взаимодействие с бэкэндом.
- Находить, на чьей стороне проблема: на клиенте или на сервере.
- Упрощать и ускорять проверки на готовых тестовых данных, не привлекая разработчиков сервера.
- Анализировать поведение мобильного приложения в различных сетевых условиях: отвала запросов, задержки, больших данных. Charles позволяет определить неверно сформированные запросы от клиента к серверу и избежать зацикленности при обращении к серверу, и, как следствие, нагревания устройства и быстрого разряда.
У Dart свой клиент для работы с сетью. Так как все запросы идут через него, то необходимые настройки для работы с прокси нужно вводить внутри приложения. Для удобства тестировщиков все нужные настройки вынесены на отдельный экран: мы в Surf используем Debug Screen, который разработали сами.
Debug Screen — это дополнительный экран настроек, который доступен только из debug-сборки и помогает в тестировании. В нём можно выбрать необходимый сервер, включить использование чтения и сохранения http-запросов в приложении, просмотреть fcm-токен устройства и не только — возможностей для тестирования предусмотрено много.
Debug Screen кастомный: разработчики добавляют на него дополнительные элементы по просьбе тестировщиков — например, поля для настройки прокси из приложения. Поэтому возможности работы с Charles у нас полные: можно подключить прокси-сервер на Debug Screen без перезапуска приложения.
Как видим, возможности при тестировании Flutter-приложений не ограничены. Всё, с чем мы привыкли работать при нативе, есть и удобно в использовании. Это не может не радовать.
Проблемы: баги фреймворка, недоработки в сторонних библиотеках, ожидаемое нативное поведение
Проблемы, с которыми мы сталкиваемся при тестировании Flutter-приложений, есть и в нативе. Сказать, что это специфичные недостатки Flutter, нельзя: в любой технологии решения задач не всегда очевидны и просты.
Расскажем, на что обращать внимание, тестируя Flutter-приложения. Предупреждён — значит вооружён.
Баги Flutter-фреймворка
При тестировании столкнулись с проблемой отображения и форматирования шрифтов на iOS: межбуквенный интервал на iOS-платформе был заметно шире, чем на Android. Это вызывало большое количество визуальных багов.
Выяснилось, что проблема в самом фреймворке. Когда наши разработчики мобильных приложений обратились к ребятам из сообщества разработчиков Flutter-фреймворка с просьбой пофиксить такой неприятный баг, вскоре вышло обновление фреймворка и отображение текста на iOS было исправлено.
Наверняка такие ситуации будут повторяться. Но нельзя назвать это большой проблемой: ребята из Flutter-комьюнити быстро реагируют на issues, поддерживают и развивают фреймворк.
Недоработки в сторонних библиотеках
На iOS версий 10 и 11 встречались недоработки реализации в библиотеках сторонних разработчиков. Например, мы исправляли баг, когда пермишен на доступ к уведомлениям всплывал сразу при запуске приложения, а не по кнопке, как планировалось по ТЗ и дизайну.
Такие проблемы могут встретиться и в кроссплатформе, и в нативе. Решаются фиксами внутри проекта, либо совместно с разработчиками библиотек.
Работа с ожидаемым нативным поведением
При длительном использовании и тестировании нативных приложений на iOS и Android легко спрогнозировать ожидание пользователя от различного поведения приложений. Так, например, возврат назад бэксвайпом на iOS — стандартный жест. А в Android он не нужен.
Системные диалоги на обеих платформах отличаются: в iOS необходимо запросить пермишен на доступ к уведомлениям, а на Android такой доступ дан по умолчанию.
Именно такие части специфики ОС зачастую приходится допиливать вручную. А иногда — выпиливать, если вдруг ожидаемое поведение для iOS платформы перекочевало в Android, как было с бэксвайпом.
В нативных приложениях проблемы вроде обновления экрана, некорректного сворачивания приложения, работы приложения, несвойственной для текущей ОС, встречаются редко: инструменты и технологии для разработки приложения на определённую платформу заведомо направлены на охват всех версий и возможностей конкретной системы.
При тестировании одного из Flutter-приложений cтолкнулись с интересной ситуацией: возможность для обновления экрана была недоступна на iOS устройствах с чёлкой — начиная с iPhoneX и выше. При этом iOS-устройства без чёлки и Android функционировали корректно.Еще один баг встретился нам на Android версии 6: при сворачивании приложение полностью выгружалось из памяти.
Такие баги были пофикшены нашими разработчиками внутри проекта.
iOS прекрасно знают свои устройства и системы, какие фишки они выпускают с новой версией ОС и какие теперь не будут работать в предыдущих версиях, на что делать акцент при обновлении того же Swift. Android осознают, что должны ориентироваться на большое количество устройств и абсолютно разные размеры экранов, и тоже знают свою специфику.
Хочется чтобы кроссплатформа содержала все тонкости реализации от нативной разработки. Конечно, недочёты при работе с Flutter ощущаются, но это не проблема: просто к кроссплатформе нужен свой подход.
Преимущества: единая кодовая база, одна команда разработки
Единая кодовая база сокращает время тестирования
Причиной багов может стать нечёткое ТЗ, отсутствие отрисованных состояний в дизайне, обратная совместимость при обновлении бэка. Заводить такие баги проще, потому что нужно создавать в два раза меньше задач, и это уже экономит время.
Искать баги и проверять фичи можно на одной платформе: с высокой долей вероятности они повторяются на обеих платформах — ведь реализация одна.
Логика новых фич на обеих платформах тоже одинакова, так как написан один код: тестирование сложных процессов внутри приложения сводится к тестированию их на одной платформе и подтверждению на другой. Мы проводим полный цикл активностей на одной платформе: делаем исследовательское тестирование, прогоны по фичам, смоук/санити/фулл, анализируем обратную связь. После этого остаётся только подтвердить качество исследовательским тестированием на другой платформе. Такой подход экономит время тестирования логики примерно в 1,3 раза.
ПримерПредположим, что аналитики, тестируя события, заметили ситуацию: по ТЗ событие должно отправиться в соответствующую систему аналитики, но оно не трекается. Такой пробел в логике однозначно говорит о том, что событие не будет отправляться на обеих платформах.
Исправив баги и убедившись на различных сценариях, что сбор аналитики в приложении работает корректно на одной из платформ (например, iOS), с большей уверенностью можем считать, что не встретим баг на другой платформе (Android), связанный с треком лишнего события или отсутствия нужного event в системе аналитики.
Если сборки на обе платформы нужно отдать заказчику в один день, выходят они одновременно и QA-инженер на проекте один, времени на проверку при нативной разработке может не хватить: цикл тестирования нужно выполнить на обеих платформах отдельно. Тестируя кроссплатформенные приложения, экономим время: регрессионное тестирование обеих платформ проходит в рамках единственного цикла.
Мы попробовали примерно оценить тестирование двух похожих проектов — один на нативных Android и iOS, второй на Flutter — и сравнили их пофично.
Анализ и тестирование проводились на одном устройстве iOS и на одном устройстве Android-платформы. Как видно на практике, Flutter действительно даёт выигрыш во времени, хоть и не в два раза. Это объяснимо: полностью убрать тестирование на одной из двух платформ нельзя. Как ни крути, у них разная специфика и ориентированность на пользователя.
При тестировании готовой фичи, не влияющей стопроцентно на специфику операционной системы и не написанной кастомно под каждую платформу отдельно, время проверки Flutter-приложения на обеих платформах сокращается примерно в 1,3—1,5 раза. Например, фичи авторизации и восстановления пароля, не имеющие специфичного поведения на разных платформах, сокращают время тестирования Flutter-версии в 1,3 раза.
Что касается фич, которые требуют кастомного поведения от каждой платформы, ждать сокращения времени не стоит. Поведение для iOS и Android ожидается разное, а значит, и тестировать обе платформы нужно полностью и отдельно друг от друга. Например, тестировать пуш-уведомления зачастую необходимо полным циклом на обеих платформах из-за различий в пермишенах, работы с подключением уведомлений, настройки пушера для отправки уведомлений на iOS и Android, а также других тонкостей реализации — чтобы поддерживалась привычная для пользователя работа с уведомлениями, соблюдались ТЗ и дизайн.
Организовать коммуникацию внутри команды проще
Когда в проекте много людей, тяжело организовать процесс так, чтобы даже малейшие тонкости не проходили мимо. Особенно, если впереди ожидается много доработок, реализаций новых фич и изменений в целом. Большая часть проблем решается, когда команда разработки одна.
Во-первых, проверять приложение легче за реализацией одной команды, чем работать с двумя разными реализациями на двух платформах. Специалист по обеспечению качества, конечно, заинтересован в том, чтобы обладать полной информацией о статусе приложения, как на платформе iOS, так и на платформе Android. При работе с Flutter это проще.
Во-вторых, в нативной разработке есть важный момент: об изменениях, которые узнала и приняла одна платформа, необходимо извещать другую платформу, что иногда забывается или теряется в ходе больших и интенсивных изменений. Такой проблемы при разработке Flutter-приложений нет: команда одна — то есть одна задача на доработку распространяется на обе платформы.
Нам нравится тестировать Flutter-приложения
Быть частью крутого сообщества
Для нас новый фреймворк — это плюс: решая нестандартные проблемы, мы расширяем кругозор. Мы находим много интересных и уникальных багов, которые развивают уровень наших навыков и возможностей при тестировании приложений.
При этом разработчики из коммьюнити Flutter-фреймворка быстро дают обратную связь по выявленным проблемам, улучшают библиотеку и позволяют нам контрибьютить в проект: мы идём вперёд вместе, и это приятно.
Быть специалистом
При работе с кроссплатформенными приложениями важно помнить о различиях операционных систем и не терять внимание к специфике платформы. Искать и видеть отличия там, где их в теории должно быть минимально, учиться тому, с чем раньше не сталкивался, — такая работа повышает профессионализм.
При разработке и тестировании нативных приложений невозможно собрать iOS приложение из, например, Android Studio или Visual Studio Code. При работе с Flutter IDE едина и для Android, и для iOS. Это круто.
Быть самостоятельным
При работе с Flutter мы в Surf сталкиваемся с очень разными проектами: от e-commerce до банкинга. Практика показала, что QA-инженер может в одиночку справляться с тестированием обеих платформ. Подключать ещё одного специалиста необходимо разве что ближе к релизу, когда темп работы увеличивается, а времени на выполнение задач остаётся всё меньше.
Flutter — шаг вперёд
Тестировать кроссплатформенные приложения не сложно. Иногда это даже быстрее и удобнее, чем работать с нативными. Все трудности, с которыми можно столкнуться, не перекрывают удобство и плюсы.
Опыт разработки и тестирования Flutter-приложений показал Surf, что этот фреймворк — большой шаг вперёд.