начнем с вопроса Что
Тестирование — это просто написание некоторой логики, чтобы сказать вам, является ли результат, который вы ожидаете от вашего кода, действительным или нет.
Например, вы можете ожидать, что при запуске приложения появится синяя кнопка, или вы можете ожидать, что значение будет увеличиваться на единицу при нажатии кнопки.
Но, конечно, здесь мы говорим об автоматизированном тестировании, так что это может быть какой-то код, который сделает это за вас и даст вам результат после взаимодействия с вашим кодом.
Важным примечанием здесь является то, что модульное тестирование или любой тип автоматического тестирования не заботятся о деталях реализации, то есть о том, как вы написали свой код и как вы реализовали какую-либо функцию или функциональность. Модульное тестирование заботится только о выводе вашего кода после того, как вы вводите его или взаимодействуете с пользовательским интерфейсом.
а теперь поговорим о том, почему
Зачем вам нужно писать тесты? Какие преимущества вы получите? Разве это не пустая трата времени?
Любое программное обеспечение, которое будет использоваться многими пользователями, будет нуждаться в частых изменениях. Возможно, чтобы исправить ошибку или добавить новую функцию. Большинство компаний-разработчиков программного обеспечения будут вносить изменения каждый день и запускать код в производство не реже одного раза в неделю, а иногда и каждый день. Таким образом, очень легко и возможно сломать что-то в пути, поэтому каждый раз, когда вы отправляете новую версию пользователям, вам нужно убедиться, что все по-прежнему работает так, как ожидалось, и ничего не сломалось неожиданно, и это практически невозможно сделать вручную из-за огромное количество вещей и сценариев, которые вам нужно протестировать.
Таким образом, модульные тесты дадут вам уверенность в том, что вы будете часто отправлять свой код, и убедитесь, что вы ничего не сломали в пути. Всякий раз, когда вам нужно объединить свой код с производственным кодом или отправить его пользователям, вы просто запустите все тесты и сразу поймете, что что-то сломалось или все еще работает нормально.
какие есть типы тестов во флаттере?
- Модульные тесты
- Интеграционные тесты
- Тесты виджетов
Чтобы помочь вам понять это, мы возьмем пример автомобиля. Предположим, мы хотим проверить, правильно ли работает наш новый автомобиль, прежде чем отправить его клиенту.
Модульные тесты:
С помощью модульных тестов вы можете протестировать единицу своего кода, например, функцию или класс. Он не охватывает взаимодействие между классами или модулями.
Для примера с автомобилем вы берете колеса или любую часть автомобиля и тестируете их отдельно в отдельной среде, а не в самой машине. Таким образом, в конце теста мы будем знать, что наши колеса работают правильно, двигатель тоже работает правильно, и то же самое со всеми остальными частями.
То же самое относится и к модульным тестам флаттера. Он не будет работать на реальном устройстве или симуляторе, это просто еще одна среда для тестирования каждой единицы нашего кода. Вот почему это очень быстро.
Интеграционные тесты:
Интеграционные тесты дают вам возможность протестировать ваши модули и взаимодействие между ними, они также будут работать на реальном устройстве или симуляторе, поэтому вы увидите, что ваше приложение работает так, как если бы вы тестировали его вручную. Этот тип теста дает больше уверенности, но требует гораздо больше времени и не идеален для запуска в конвейере CI/CD.
Итак, давайте вернемся к примеру с автомобилем, на этот раз вы можете представить, что мы возвращаем двигатель и колеса и тестируем их оба в машине, а также взаимодействие между ними. Поэтому, когда двигатели работают, колеса также должны начать вращаться.
Тесты виджетов:
На самом деле тесты виджетов во флаттере дают вам возможность тестировать свои виджеты. Взаимодействуйте с ними, например, нажимайте кнопку, вводите текст в текстовое поле и т. д. Вы можете использовать их для написания модульных тестов, если вы хотите протестировать каждый виджет отдельно, или вы также можете использовать его внутри интеграционных тестов для тестирования дырочного модуля. Таким образом, вы можете рассматривать его как инструмент от флаттера для тестирования ваших виджетов, если вы используете его с модульными тестами, он будет работать в отдельной среде, а если вы используете его с интеграционными тестами, он будет запускать реальное устройство/симулятор.
Подробнее о типах тестов можно прочитать здесь https://docs.flutter.dev/testing
Теперь давайте переместим собственно написание теста
Это репозитории git, которые мы будем использовать в этой серии.
Мы начнем с написания простого модульного теста для проверки нашего CounterService.
Оба теста должны находиться в папке с тестами и иметь _test.dart в конце, чтобы при запуске «flutter test» он распознавал эти тесты и запускал их. Среда IDE также распознает их таким образом.
NB: рекомендуется структурировать тестовую папку так же, как папку lib. Таким образом, будет очень легко узнать, что вы тестировали, и сделать его легким для чтения.
После того, как вы клонируете репозиторий, код будет в main.dart. Разобраться несложно, это просто виджет с 2-мя кнопками и текстом. Каждая кнопка будет либо увеличиваться, либо уменьшаться. Использование сервиса Counter, который мы будем использовать только для того, чтобы понять разницу между модульным тестом и тестом виджета.
import 'package:flutter/material.dart'; import 'package:test_p1/services/counter_services.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Testing', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter testing'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final CounterService _counterService = CounterService(); void _decrementCounter() { setState(() { _counterService.decrement(); }); } void _incrementCounter() { setState(() { _counterService.increment(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '${_counterService.counter}', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), FloatingActionButton( onPressed: _decrementCounter, tooltip: 'Decrement', child: const Icon(Icons.remove), ), ], ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
Первый тест, который мы исправим, — это модульный тест для проверки нашего CounterService. Как мы уже говорили, модульный тест просто используется для проверки единицы логики. Это может быть функция, класс..
В нашем случае мы протестируем наш класс CounterService. counter_service_test.dart
import 'package:flutter_test/flutter_test.dart'; import 'package:test_p1/services/counter_services.dart'; void main() { late CounterService counterService; group('CounterService', () { setUp(() { counterService = CounterService(); }); test('should increment counter', () { counterService.increment(); expect(counterService.counter, 1); }); test('should decrement counter', () { counterService.decrement(); expect(counterService.counter, -1); }); }); }
В этом тесте мы поймем несколько вещей. Прежде всего, нам нужно добавить метод main, который будет запускаться первым.
Как правило, первое, с чего мы начинаем любой тест, — это настройка наших зависимостей. В этом случае у нас есть одна зависимость, которая называется CounterService.
Итак, CounterService поздно сообщает dart, что мы обязательно инициализируем counterService, и он не будет нулевым.
group(‘description’,(){}) поможет нам разделить тесты на группы.
У нас также есть несколько других вспомогательных методов, таких как:
setUp((){})
Если вы поместите этот метод внутри основного метода, он будет выполняться перед каждым тестом().
В нашем случае нам нужен новый экземпляр для каждого теста, поэтому setUp как раз поможет нам в этом.
Если вы поместите этот метод в группу (() {}), он будет выполняться перед каждым тестом в этой группе.
setUpAll((){})
Это то же самое, что и setUp, но запускается только один раз перед первым тестом. Если он находится внутри main, он будет запущен перед первым тестом только один раз. Если он находится внутри группы, он будет запущен только один раз перед первым тестом внутри этой группы.
снять((){})
то же, что и setUp, но вместо этого он запускается после тестов.
TearDownAll((){})
то же, что и setUpAll, но запустится после последнего теста.
тест('Описание',(){})
Здесь будет наш тест. Вы добавляете «Описание», описывающее тест, и вы просто добавляете вещь, которую будете тестировать, и добавляете метод expect(), который будет проверять, соответствует ли фактическое значение тому, что мы ожидали, или нет. В нашем случае будет ли счетчик 1 после запуска метода увеличения или нет. То же самое с декрементом.
У метода expect может быть так много сопоставителей, которые мы можем использовать, например throwsA‹Exception› или списки сравнения, карты и многое другое.
Запуск тестов
Для запуска тестов мы можем запустить команду флаттера
flutter test
Или вы также можете запустить их из IDE (например, VS Code). На боковой панели вы найдете этот логотип. Вы можете запускать их либо в режиме отладки, чтобы использовать точки останова и все остальное в режиме отладки, и вы можете видеть результаты в консоли отладки, либо в обычном режиме.
Вы также можете запустить каждый тест отдельно с помощью этого инструмента или даже с помощью команды, указав имя файла.
Вы также увидите эту утилиту в vscode поверх каждого теста.
Окончательно
Следующий тип теста мы закончим в отдельной статье. Есть много вещей, которым можно научить в тесте виджетов, и это нужно объяснить отдельно.
Сейчас нам нужно понять, что существуют разные типы тестов.
Нам нужно знать, что такое group, test, expect, setUp, setUpAll, tearDown, tearDownAll делают это, и с помощью этих помощников вы можете написать практически любой модульный тест. Немного продвинутых вещей, таких как тестирование потоков или асинхронный код, также будут объяснены позже, потому что это всего лишь руководство для начинающих.