Flutter позволяет вам писать простые и понятные тесты для разных частей приложения.
Сегодня мы попробуем написать несколько unit тестов, которые используются для тестирования классов, методов и отдельных функций.
Также мы попробуем использовать библиотеку Mockito
, которая позволяет создавать фейковые реализации.
Ну что ж, приступаем к тестированию!
Наш план
Часть 1 - введение в разработку, первое приложение, понятие состояния;
Часть 2 - файл pubspec.yaml и использование flutter в командной строке;
Часть 3 - BottomNavigationBar и Navigator;
Часть 4 - MVC. Мы будем использовать именно этот паттерн, как один из самых простых;
Часть 5 - http пакет. Создание Repository класса, первые запросы, вывод списка постов;
Часть 6 - работа с формами, текстовые поля и создание поста.
Часть 7 - работа с картинками, вывод картинок в виде сетки, получение картинок из сети, добавление своих в приложение;
Часть 8 - создание своей темы, добавление кастомных шрифтов и анимации;
Часть 9 (текущая статья) - немного о тестировании;
Добавления необходимых зависимостей
Нам понадобиться два дополнительных пакета mockito
и build_runner
, поэтому добавим их:
# зависимости для разработки
# в данном случае подключено тестирование
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.10
build_runner: ^2.0.4
Теперь мы можем приступать к тестированию
Пишем первый тест
В качестве объекта тестирования будет небольшой класс Stack
:
class Stack<T> {
final stack = <T>[];
void push(T t) {
stack.add(t);
}
T? pop() {
if (isEmpty) {
return null;
}
return stack.removeLast();
}
bool get isEmpty => stack.isEmpty;
}
Обратите внимание: класс Stack
является обобщенным.
В корневой директории нашего проекта есть папка test, которая предназначена для
тестов.
Создадим в ней новый файл stack_test.dart
:
import 'package:flutter_test/flutter_test.dart';
import 'package:json_placeholder_app/helpers/stack.dart';
void main() {
// группа тестов
group("Stack", () {
// первый тест на пустой стек
test("Stack should be empty", () {
// expect принимает текущее значение
// и сравнивает его с правильным
// если значения не совпадают, тест не пройден
expect(Stack().isEmpty, true);
});
test("Stack shouldn't be empty", () {
final stack = Stack<int>();
stack.push(5);
expect(stack.isEmpty, false);
});
test("Stack should be popped", () {
final stack = Stack<int>();
stack.push(5);
expect(stack.pop(), 5);
});
test("Stack should be work correctly", () {
final stack = Stack<int>();
stack.push(1);
stack.push(2);
stack.push(5);
expect(stack.pop(), 5);
expect(stack.pop(), 2);
expect(stack.isEmpty, false);
});
});
}
Довольно просто! Не правда ли?
На самом деле, это один из типов тестирования, который называется unit (модульное).
Также Flutter поддерживает:
Widget тестирование
Интеграционное тестирование
В данной статье мы рассмотрим только unit тестирование.
Давайте выполним наши тесты командой flutter test test/stack_test.dart:
Успешно!
Тестируем получение постов
Сначала видоизменим метод fetchPosts
:
Future<PostList> fetchPosts({http.Client? client}) async {
// сначала создаем URL, по которому
// мы будем делать запрос
final url = Uri.parse("$SERVER/posts");
// делаем GET запрос
final response = (client == null) ? await http.get(url) : await client.get(url);
// проверяем статус ответа
if (response.statusCode == 200) {
// если все ок то возвращаем посты
// json.decode парсит ответ
return PostList.fromJson(json.decode(response.body));
} else {
// в противном случае вызываем исключение
throw Exception("failed request");
}
}
Теперь переходим к написанию самого теста.
Мы будем использовать mockito
для создания фейкового http.Client'
а
Создадим файл post_test.dart
в папке tests
:
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:json_placeholder_app/data/repository.dart';
import 'package:json_placeholder_app/models/post.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
// данный файл будет сгенерирован
import 'post_test.mocks.dart';
// аннотация mockito
@GenerateMocks([http.Client])
void main() {
// создаем наш репозиторий
final repo = Repository();
group("fetchPosts", () {
test('returns posts if the http call completes successfully', () async {
// создаем фейковый клиент
final client = MockClient();
// ответ на запрос
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts')))
.thenAnswer((_) async => http.Response('[{"userId": 1, "id": 2, "title": "Title", "content": "Content"}]', 200));
// проверяем корректность работы fetchPosts
// при удачном выполнении
final postList = await repo.fetchPosts(client: client);
expect(postList, isA<PostList>());
expect(postList.posts.length, 1);
expect(postList.posts.first.title, "Title");
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// генерация ошибки
when(client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts')))
.thenAnswer((_) async => http.Response('Not Found', 404));
// проверка на исключение
expect(repo.fetchPosts(client: client), throwsException);
});
});
}
Перед запуском теста необходимо сгенерировать post_test.mocks.dart
файл:
flutter pub run build_runner build
После этого выполняем наши тесты командой flutter test test/post_test.dart
:
Вуаля!
Заключение
Мы разобрали один из самых простых и известных типов тестирования - unit (модульное).
Как уже было отмечено, Flutter позволяет отдельно тестировать виджеты, а также проводить полноценное тестирование с применением интеграционных тестов.
Полезные ссылки:
Github исходный код
An introduction to unit testing
Mock dependencies using Mockito
Testing Flutter apps
Всем хорошего кода!