Тестирование виджетов во Flutter: написание независимых, читаемых и быстрых тестов

Тестирование виджетов во Flutter: написание независимых, читаемых и быстрых тестов

13 января 2024 г.

Во Flutter есть 3 типа тестов:

  1. Юнит-тесты — обычные модульные тесты, в которых мы пишем тесты для наших функций, методов и классов.
  2. Тестирование виджетов — мы пишем тесты для наших виджетов и проверяем, совпадают ли они визуально, также можно проверить событие, например клик.
  3. Интеграционные тесты — как правило, все приложение тестируется и запускается на реальных устройствах или в эмуляторе.
  4. Из перечисленных тестов разберем тесты Виджета. Цель состоит в том, чтобы протестировать виджет, поскольку в Flutter все является виджетами.

    Тесты должны быть:

    • независимый;
    • не следует создавать виджеты;
    • быть простым, читабельным и быстрым;

    Для тестовых виджетов метод testWidgets представлен описанием основных параметров, где можно написать описание в строковом формате, а следующий класс параметров WidgetTester — это класс, который программно взаимодействует с виджетами и тестовой средой.

    testWidgets('Test description', (WidgetTester widgetTester) async {  
      //...
    });
    

    Возьмем, к примеру, приведенный ниже код с полем ввода и кнопкой «Войти»:

    class SignInScreen extends StatefulWidget {  
      const SignInScreen({super.key});  
    
      @override  
      State<StatefulWidget> createState() => _SignInScreenState();  
    }  
    
    class _SignInScreenState extends State<SignInScreen> {  
    
      final TextEditingController _textEditingController = TextEditingController();  
    
      @override  
      Widget build(BuildContext context) {  
        return Scaffold(  
          appBar: AppBar(title: const Text('Check email'),),  
          body: Padding(  
            padding: const EdgeInsets.all(8.0),  
            child: Column(  
              children: [  
                TextField(  
                  controller: _textEditingController,  
                  key: const ValueKey('email_field'),  
                  decoration: const InputDecoration(hintText: 'Enter e-mail'),  
                ),  
                const SizedBox(height: 24,),  
                ElevatedButton(onPressed: (){  
                  Navigator.push(context, MaterialPageRoute(builder: (context) => const SuccessScreen()));  
                }, child: const Text('Sing In'))  
              ],  
            ),  
          ),  
        );  
      }  
    
    }
    

    class MyApp extends StatelessWidget {  
      const MyApp({super.key});  
    
      @override  
      Widget build(BuildContext context) {  
        return MaterialApp(  
          title: 'Flutter Widget test',  
          theme: ThemeData(  
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),  
            useMaterial3: true,  
          ),  
          home: const SignInScreen(),  
        );  
      }  
    }
    

    И давайте напишем тест виджета:

    void main() {  
      testWidgets('Widget test', (WidgetTester widgetTester) async {  
        await widgetTester.pumpWidget(const MaterialApp(home: SignInScreen(),)); 
    
        Finder title = find.text('Check email');  
        expect(title, findsOneWidget);  
      });  
      testWidgets('Test input field', (WidgetTester widgetTester) async {  
        await widgetTester.pumpWidget(const MaterialApp(home: SignInScreen(),)); 
    
        Finder emailTextField = find.byKey(const ValueKey('email_field'));  
        expect(emailTextField, findsOneWidget);  
      });  
      testWidgets('Test sign in button', (WidgetTester widgetTester) async {  
        await widgetTester.pumpWidget(const MaterialApp(home: SignInScreen(),));
    
        Finder signInButton = find.byType(ElevatedButton);    
        expect(signInButton, findsOneWidget);  
      });  
    }
    

    Метод pumpWidget визуализирует переданный виджет. Далее с помощью Finder ищем нужный нам виджет. Вы можете искать по-разному и использовать разные средства поиска. Вот наиболее распространенные из них (их гораздо больше). Вы также можете легко написать свой искатель.

    • find.text() — ищет текст;
    • find.byKey() — ищет виджет по ключу;
    • find.byType() — ищет виджет по типу;
    • find.byIcon() — ищет виджет типа Icon;
    • find.byWidgetPredicate() — ищет виджет по предикату;

    В приведенном выше примере я использую три метода для заголовка, используя текст find.text('Проверить электронную почту'), для поля ввода, используя ключ find.byKey(const ValueKey('email_field')), а для кнопки - тип find.byType( ElevatedButton).

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

    Ниже приведены наиболее, на мой взгляд, используемые:

    • findsOneWidget — сравнивает, что Finder находит только один виджет;
    • findsWidgets — сравнивает, что Finder находит хотя бы один виджет;
    • isSameColorAs(Color color) — сравнивает, что объект имеет определенный цвет;
    • findsNothing — сравнивает, что Finder не находит виджет;
    • isNotNull — сравнивает, что объект не имеет значения NULL;

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

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

    Если тест пройден успешно, мы получим следующий результат:

    Спасибо за внимание!


    Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE