[Перевод] Тестирование пользовательского интерфейса | Flutter

Тестирование пользовательского интерфейса | Flutter

Тестирование пользовательского интерфейса | Flutter

Привет, если вы на пути изучения Flutter/Dart или вам просто интересно почитать про путь изучения, подписывайтесь на мой канал в telegram, буду рад вас видеть! А сегодня поговорим про работу тестирования приложения во Flutter!

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

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

Вы узнаете, как:
• Понимать автоматизированное тестирование виджетов
• Интегрировать автоматизированное тестирование виджетов
• Использовать драйвер Flutter
• Работа с пакетом тестирования Firebase

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

Автоматическое тестирование виджетов во Flutter

Проблема

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

Решение

Используйте тестирование виджетов, чтобы обеспечить дополнительную надежность вашего приложения. В частности, вы можете включить тесты для таких элементов виджета, как кнопки FloatingActionButtons, текст и ListViews. Добавление этой функциональности в ваше приложение обеспечит дополнительный охват для уменьшения общего количества дефектов. На рисунке 14–1 приложение boilerplate counter используется для иллюстрации методов автоматизированного тестирования.

Рисунок 14-1. Тестирование пользовательского интерфейса Flutter

Рисунок 14–1. Тестирование пользовательского интерфейса Flutter

Для тестирования шаблонного приложения, созданного Flutter, как показано на рисунке 14–1, нам потребуется некоторый код. Следующий тестовый код используется для проверки экранных элементов, связанных с представленным приложением:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test_widget_app/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());
    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);
    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();
    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

Обсуждение

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

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

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

Tester.pumpWidget используется для увеличения тестируемого экрана. Если у вас есть приложение с несколькими экранами, обратите внимание на корректный вызов этого метода. В большинстве примеров для этого используется класс MyApp, который связан с MaterialApp:

// Build our app and trigger a frame.
 await tester.pumpWidget(const MyApp());

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

// Build our app and trigger a frame.
 await tester.pumpWidget(MaterialApp(detail: DetailPage()));

Доступ к экранной информации может быть обеспечен несколькими способами. В примере кода экранные элементы чрезмерно заполнены, поэтому достаточно использовать метод find.text. Если ваше приложение имеет более загруженный экран и включает больше элементов, потребуется альтернативный метод. К счастью, метод find включает в себя ряд альтернатив, подходящих для большинства ситуаций. Я настоятельно рекомендую добавить метод find в закладки, если вы собираетесь создавать тесты виджетов. Лично я использую find.byType, find.byWidget и find.text больше всего из всех доступных вариантов.

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

Выполнение автоматического тестирования виджетов

Проблема

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

Решение

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

В примере определены поля имени пользователя и пароля. Тест виджета использует методы pumpWidget и pump для взаимодействия с виджетом:

void main(){
 testLoginWidget("should allow login”, (WidgetTester testWorker) async {
 // Arrange
 final testUsername = find.byKey(ValueKey("testUsername”));
 final testPassword = find.byKey(ValueKey("testPassword”));
 final testLoginBtn = find.byKey(ValueKey("testLoginBtn”));
 // Act
 await testWorker.pumpWidget(MaterialApp(home: Home()));
 await testWorker.enterText(testUsername, "username”);
 await testWorker.enterText(testPassword, "password”);
 await testWorker.tap(testLoginBtn);
 Await testWorker.pump();
 // Assert
 expect(find.text("Login credentials supplied”), findsOneWidget);
 });
}

Обсуждение

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

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

Выполнение интеграционного тестирования с драйвером Flutter

Проблема

Вам нужен способ протестировать все приложение целиком.

Решение

Используйте Flutter Driver, поскольку он предоставляет пакет драйверов интерфейса, который позволит автоматизировать взаимодействие с приложением. Взаимодействие автоматизирует обычные действия, которые выполнялись бы пользователем приложения, например, ввод текста, выбор элементов и нажатие кнопок.

Чтобы начать интеграционный тест, создайте базовый интерфейс в Flutter:

import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';

void main() {
 enableFlutterDriverExtension();
 runApp(MyApp());
}
class MyApp extends StatelessWidget {
 …
}
class MyWidget extends StatelessWidget {
 …
}

Создайте интеграционное тестовое приложение:

final txtUsername = find.byType(Text);
final btnAddition = find.byType(FloatingActionButton);

FlutterDriver driver;
setUpAll(()) async {
 driver = await FlutterDriver.connect();
});
tearDownAll(()) async {
 if (driver != null) {
 driver.close();
 }
}
test ('Should enter username and press button', ()async {
 await driver.tap(txtUsername);
 await driver.enterText("Martha Kent")
 await driver.tap(btnAddition);
 await driver.waitFor(find.text("Welcome"));
});

Обсуждение

В примере кода драйвер Flutter добавлен в приложение для обеспечения необходимого средства тестирования интеграции.

Чтобы инициировать интеграционный тест, запустите процесс, выполнив следующие действия в командной строке:

flutter drive --target=test_driver/main.dart

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

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

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

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

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

Тестирование совместимости устройств Android/iOS

Проблема

Вы хотите обеспечить совместимость устройств с помощью автоматического тестирования.

Решение

Используйте роботизированные тесты Firebase Test Lab, чтобы обеспечить автономный подход к тестированию. Опция Firebase не требует дополнительной настройки и использует двоичный файл приложения в качестве входных данных.

Обсуждение

Firebase Test Lab предоставляет богатый набор инструментов тестирования для вашего приложения.

Обратите внимание, что на момент написания статьи набор тестов Firebase специально ориентирован на Android и iOS. Поэтому вы, возможно, захотите рассмотреть другие варианты, если вы нацелены на другие платформы, например, Web, Windows или Linux.

Если у вас есть потребность улучшить совместимость с мобильными устройствами, тестовая лаборатория Firebase, скорее всего, станет оптимальным решением для вас и вашей команды. Как и в случае с другими продуктами Firebase, подход к выставлению счетов основан на Spark (ограниченные тесты на 5 физических/10 виртуальных устройствах) и Blaze (выставление счетов поминутно).

Тестовая лаборатория Firebase основана на нескольких продуктах. Каждый продукт нацелен на выполнение ключевых этапов жизненного цикла обеспечения качества. Использование тестовой лаборатории означает, что приложения можно тестировать без дополнительных усилий по кодированию, загрузив двоичный файл приложения Flutter. Firebase Test Lab может интегрироваться с существующим рабочим процессом для процессов сборки Android для организации разработки приложений. Результирующие тесты могут выполняться как на физических, так и на виртуальных устройствах для обеспечения обратной связи в реальном мире.

Пакет приложений принимает приложение с отладочным кодом в качестве входных данных. В случае Android для запуска процесса необходимо будет предоставить набор пакетов Android (APK)/Android App Bundle (AAB). Двоичный файл Android будет найден в каталоге сборки. Кроме того, виртуальные устройства, на которых будет развернуто приложение, также необходимо будет выбрать из очень широкого спектра устройств.

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

Robo-тесты на Android можно использовать для автоматической навигации по приложению и выполнения записи журнала, подходящей для глубокого устранения неполадок. Robo-тесты предоставляют много информации для каждого тестового запуска, такой как скриншоты и видео выполненных тестов. Если вы ориентируетесь на устройства iOS, XCTest выполняет модульные тесты, тесты производительности и тесты пользовательского интерфейса для проектов на базе Xcode.

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

Habrahabr.ru прочитано 20132 раза