В предыдущем посте мы обсуждали использование find.byPredicate
для более точного и пуленепробиваемого тестирования виджетов.
Flutter: Повышение эффективности тестирования
В этом посте мы быстро пройдемся по той же логике, но для модульного тестирования, связанного с данными.
Для 95% моих моделей данных я использую freezed, замечательный пакет, который значительно упрощает жизнь, когда дело доходит до классов данных, включая добавление нескольких полезных методов, а также упрощает модульное тестирование за счет обработки равенства, чего я раньше добивался вручную и с некоторой помощью equatable.
Однако при использовании freezed
есть немного шаблонов, так как он работает с генерацией кода.
enum MenuItemKey {logout, dashboard}
class MenuItem {
final MenuItemKey key;
final String localeKey;
final PageRouteInfo route;
MenuItem({
required this.key,
required this.localeKey,
required this.route,
});
static List<MenuItem> menuItems = [...]
static List<MenuItem> itemsToDisplay(List<MenuItemKey> excludedKeys) {
return menuItems
.where((element) => !excludedKeys.contains(element.key))
.toList();
}
}
Возьмем, к примеру, этот класс. Добавление freezed
было бы простым делом, добавить 2-3 строки кода и дополнительный сгенерированный файл, однако я посчитал это ненужным, поскольку это модель, которую я создал для простого управления навигационным списком, здесь нет данных для тестирования, нет связи с API, нет необходимости в равенстве или полезных методах и т.д.
В моем приложении есть меню, которое получает список menuItems
, и с его помощью я генерирую элементы навигации.
Однако я все еще хочу иметь возможность точно протестировать itemsToDisplay
, 1 бит логики, прикрепленный к этому классу, по умолчанию все элементы основаны на функции генерации, однако не все экраны имеют все элементы навигации. Например, на экране входа в систему не нужна кнопка Logout.
Тест…
Как и в случае с виджетами, мы можем использовать predicate
как часть нашей функции expect, чтобы углубляться в модели данных и сравнивать результаты с определенными значениями.
test('should return only logout menu item', () async {
//act
final result = MenuItem.itemsToDisplay([
MenuItemKey.dashboard,
MenuItemKey.support,
MenuItemKey.profile,
]);
//assert
expect(
result,
predicate(
(List<MenuItem> items) =>
items.length == 1 && items.first.key == MenuItemKey.logout,
),
);
});
В приведенном выше примере у нас было 4 элемента в списке menuItems
, и мы приняли решение отфильтровать все, кроме logout
.
Для того чтобы проверить, работает ли наша функция, мы используем predicate
, который принимает функцию в качестве ожидания, к которой мы имеем тип данных, который мы ожидаем, а затем возвращает проверку, которая дает булев результат.
В данном случае у нас был список, содержащий 4 пункта меню, мы исключили 3 из них, поэтому мы проверяем, что в нашем списке только 1 пункт, и чтобы убедиться, что правильный пункт остался, мы проверяем, что первый (единственный) пункт в списке имеет ключ MenuItemKey.logout
, который является значением перечисления, присвоенным пункту навигации.
Заключение…
Как вы видите, find.byPredicate
и predicate
— это два очень полезных метода, о которых мы, тестировщики, должны помнить при написании тестов. Они могут помочь в менее стандартных случаях использования и позволят нам написать более качественный тест.
Надеюсь, вам было интересно, и если у вас есть вопросы, замечания или улучшения, не стесняйтесь оставлять комментарии. Приятного вам путешествия по разработке на Flutter 😀
Если вам понравилось, то лайк будет просто замечательным.
Спасибо за прочтение.
Если вы хотите продолжить тему модульного тестирования, посмотрите:
- Тестирование виджетов: Работа с ошибками переполнения Renderflex
- Flutter: Повышение уровня тестирования