1. Wprowadzenie
Flutter to opracowany przez Google zestaw narzędzi interfejsu do tworzenia pięknych, natywnie skompilowanych aplikacji na urządzenia mobilne, komputery i komputery przy użyciu jednej bazy kodu.
W ramach tego ćwiczenia w programie utworzysz i przetestujesz prostą aplikację Flutter. Aplikacja będzie używać pakietu Provider do zarządzania stanem.
Czego się nauczysz
- Jak tworzyć testy widżetów za pomocą platformy do testowania widżetów
- Jak utworzyć test integracji, aby przetestować interfejs i działanie aplikacji za pomocą biblioteki
integration_test
- Jak testować klasy danych (dostawcy) za pomocą testów jednostkowych
Co utworzysz
W tym ćwiczeniu w Codelabs zaczniesz od utworzenia prostej aplikacji z listą elementów. Udostępniamy Ci kod źródłowy, by ułatwić Ci rozpoczęcie testowania. Aplikacja obsługuje te operacje:
- Dodaję elementy do ulubionych
- Wyświetlanie listy ulubionych
- Usuwanie elementów z listy ulubionych
Po zakończeniu pracy aplikacji utworzysz te testy:
| GIF aplikacji działającej na Androidzie |
Czego chcesz się dowiedzieć z tego ćwiczenia z programowania?
2. Konfigurowanie środowiska programistycznego Flutter
Aby ukończyć ten moduł, potrzebujesz 2 oprogramowania: pakietu SDK Flutter i edytora.
Ćwiczenie z programowania możesz uruchomić na dowolnym z tych urządzeń:
- Fizyczne urządzenie z Androidem lub iOS podłączone do komputera i ustawione w trybie programisty.
- Symulator iOS (wymaga zainstalowania narzędzi Xcode).
- Emulator Androida (wymaga skonfigurowania Android Studio).
- Przeglądarka (do debugowania wymagany jest Chrome).
- Aplikacja komputerowa w systemie Windows, Linux lub macOS Programowanie należy tworzyć na platformie, na której zamierzasz wdrożyć usługę. Jeśli więc chcesz opracować aplikację komputerową dla systemu Windows, musisz to zrobić w tym systemie, aby uzyskać dostęp do odpowiedniego łańcucha kompilacji. Istnieją wymagania związane z konkretnymi systemami operacyjnymi, które zostały szczegółowo omówione na stronie docs.flutter.dev/desktop.
3. Pierwsze kroki
Utwórz nową aplikację Flutter i zaktualizuj zależności
Skupia się on na testowaniu aplikacji mobilnej Flutter. Szybko utworzysz aplikację do przetestowania, korzystając z plików źródłowych, które skopiujesz i wkleisz. Pozostała część ćwiczeń w Codelabs będzie poświęcona poznawaniu różnych rodzajów testów.
Utwórz prostą aplikację Flutter na podstawie szablonu, postępując zgodnie z instrukcjami w artykule Pierwsze kroki z pierwszą aplikacją Flutter lub korzystając z wiersza poleceń w podany niżej sposób.
$ flutter create --empty testing_app Creating project testing_app... Resolving dependencies in `testing_app`... Downloading packages... Got dependencies in `testing_app`. Wrote 128 files. All done! You can find general documentation for Flutter at: https://docs.flutter.dev/ Detailed API documentation is available at: https://api.flutter.dev/ If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev In order to run your empty application, type: $ cd testing_app $ flutter run Your empty application code is in testing_app/lib/main.dart.
Dodaj zależności wydawcy w wierszu poleceń.
provider
do łatwego zarządzania stanem,integration_test
– do samodzielnego testowania kodu Flutter na urządzeniach i emulatorach;flutter_driver
– zaawansowany interfejs API do testowania aplikacji Flutter, które działają na prawdziwych urządzeniach i emulatorach,test
z ogólnymi narzędziami testowymi,go_router
do obsługi nawigacji po aplikacji.
$ cd testing_app $ flutter pub add provider go_router dev:test 'dev:flutter_driver:{"sdk":"flutter"}' 'dev:integration_test:{"sdk":"flutter"}' Resolving dependencies... Downloading packages... + _fe_analyzer_shared 67.0.0 (68.0.0 available) + analyzer 6.4.1 (6.5.0 available) + args 2.5.0 + convert 3.1.1 + coverage 1.7.2 + crypto 3.0.3 + file 7.0.0 + flutter_driver 0.0.0 from sdk flutter + flutter_web_plugins 0.0.0 from sdk flutter + frontend_server_client 4.0.0 + fuchsia_remote_debug_protocol 0.0.0 from sdk flutter + glob 2.1.2 + go_router 14.0.2 + http_multi_server 3.2.1 + http_parser 4.0.2 + integration_test 0.0.0 from sdk flutter + io 1.0.4 + js 0.7.1 leak_tracker 10.0.4 (10.0.5 available) leak_tracker_flutter_testing 3.0.3 (3.0.5 available) + logging 1.2.0 material_color_utilities 0.8.0 (0.11.1 available) meta 1.12.0 (1.14.0 available) + mime 1.0.5 + nested 1.0.0 + node_preamble 2.0.2 + package_config 2.1.0 + platform 3.1.4 + pool 1.5.1 + process 5.0.2 + provider 6.1.2 + pub_semver 2.1.4 + shelf 1.4.1 + shelf_packages_handler 3.0.2 + shelf_static 1.1.2 + shelf_web_socket 1.0.4 + source_map_stack_trace 2.1.1 + source_maps 0.10.12 + sync_http 0.3.1 + test 1.25.2 (1.25.4 available) test_api 0.7.0 (0.7.1 available) + test_core 0.6.0 (0.6.2 available) + typed_data 1.3.2 + watcher 1.1.0 + web 0.5.1 + web_socket_channel 2.4.5 + webdriver 3.0.3 + webkit_inspection_protocol 1.2.1 + yaml 3.1.2 Changed 44 dependencies! 9 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
Do pliku pubspec.yaml powinny zostać dodane te zależności:
pubspec.yaml
name: testing_app description: "A new Flutter project." publish_to: 'none' version: 0.1.0 environment: sdk: '>=3.4.0-0 <4.0.0' dependencies: flutter: sdk: flutter go_router: ^14.0.2 provider: ^6.1.2 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.0 test: ^1.25.2 flutter_driver: sdk: flutter integration_test: sdk: flutter flutter: uses-material-design: true
Otwórz projekt w wybranym edytorze kodu i uruchom aplikację. Możesz też uruchomić je w wierszu poleceń w następujący sposób:
$ flutter run
4. Utwórz aplikację
Następnie możesz utworzyć aplikację, aby ją przetestować. Aplikacja zawiera te pliki:
lib/models/favorites.dart
– tworzy klasę modelu dla listy ulubionychlib/screens/favorites.dart
– tworzy układ listy ulubionychlib/screens/home.dart
– tworzy listę elementówlib/main.dart
– główny plik, na którym uruchamia się aplikacja;
Najpierw utwórz model Favorites
w obszarze lib/models/favorites.dart
.
Utwórz w katalogu lib
nowy katalog o nazwie models
, a następnie utwórz nowy plik o nazwie favorites.dart
. Dodaj do niego ten kod:
lib/models/favorites.dart
import 'package:flutter/material.dart';
/// The [Favorites] class holds a list of favorite items saved by the user.
class Favorites extends ChangeNotifier {
final List<int> _favoriteItems = [];
List<int> get items => _favoriteItems;
void add(int itemNo) {
_favoriteItems.add(itemNo);
notifyListeners();
}
void remove(int itemNo) {
_favoriteItems.remove(itemNo);
notifyListeners();
}
}
Dodaj stronę Ulubione w aplikacji lib/screens/favorites.dart
.
W katalogu lib
utwórz nowy katalog o nazwie screens
, a następnie utwórz w nim nowy plik o nazwie favorites.dart
. Dodaj do niego ten kod:
lib/screens/favorites.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/favorites.dart';
class FavoritesPage extends StatelessWidget {
const FavoritesPage({super.key});
static String routeName = 'favorites_page';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Favorites'),
),
body: Consumer<Favorites>(
builder: (context, value, child) => ListView.builder(
itemCount: value.items.length,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => FavoriteItemTile(value.items[index]),
),
),
);
}
}
class FavoriteItemTile extends StatelessWidget {
const FavoriteItemTile(this.itemNo, {super.key});
final int itemNo;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('favorites_text_$itemNo'),
),
trailing: IconButton(
key: Key('remove_icon_$itemNo'),
icon: const Icon(Icons.close),
onPressed: () {
Provider.of<Favorites>(context, listen: false).remove(itemNo);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Removed from favorites.'),
duration: Duration(seconds: 1),
),
);
},
),
),
);
}
}
Dodaj stronę główną w aplikacji lib/screens/home.dart
W katalogu lib/screens
utwórz kolejny plik o nazwie home.dart
. W lib/screens/home.dart
dodaj ten kod:
lib/screens/home.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../models/favorites.dart';
import 'favorites.dart';
class HomePage extends StatelessWidget {
static String routeName = '/';
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Testing Sample'),
actions: <Widget>[
TextButton.icon(
onPressed: () {
context.go('/${FavoritesPage.routeName}');
},
icon: const Icon(Icons.favorite_border),
label: const Text('Favorites'),
),
],
),
body: ListView.builder(
itemCount: 100,
cacheExtent: 20.0,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => ItemTile(index),
),
);
}
}
class ItemTile extends StatelessWidget {
final int itemNo;
const ItemTile(this.itemNo, {super.key});
@override
Widget build(BuildContext context) {
var favoritesList = Provider.of<Favorites>(context);
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('text_$itemNo'),
),
trailing: IconButton(
key: Key('icon_$itemNo'),
icon: favoritesList.items.contains(itemNo)
? const Icon(Icons.favorite)
: const Icon(Icons.favorite_border),
onPressed: () {
!favoritesList.items.contains(itemNo)
? favoritesList.add(itemNo)
: favoritesList.remove(itemNo);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(favoritesList.items.contains(itemNo)
? 'Added to favorites.'
: 'Removed from favorites.'),
duration: const Duration(seconds: 1),
),
);
},
),
),
);
}
}
Zastąp zawartość pola lib/main.dart
Zastąp zawartość pola lib/main.dart
tym kodem:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'models/favorites.dart';
import 'screens/favorites.dart';
import 'screens/home.dart';
void main() {
runApp(const TestingApp());
}
final _router = GoRouter(
routes: [
GoRoute(
path: HomePage.routeName,
builder: (context, state) {
return const HomePage();
},
routes: [
GoRoute(
path: FavoritesPage.routeName,
builder: (context, state) {
return const FavoritesPage();
},
),
],
),
],
);
class TestingApp extends StatelessWidget {
const TestingApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Favorites>(
create: (context) => Favorites(),
child: MaterialApp.router(
title: 'Testing Sample',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
),
useMaterial3: true,
),
routerConfig: _router,
),
);
}
}
Aplikacja jest gotowa, ale nie została przetestowana.
Uruchom aplikację. Powinien wyglądać podobnie do tego:
Aplikacja wyświetla listę elementów. Kliknij ikonę w kształcie serca w dowolnym wierszu, aby wypełnić serce i dodać przedmiot do listy ulubionych. Kliknięcie przycisku Ulubione na urządzeniu AppBar
powoduje przejście do drugiego ekranu z listą ulubionych.
Aplikacja jest teraz gotowa do testowania. W następnym kroku zaczniesz testować aplikację.
5. Testowanie jednostkowe dostawcy
Zaczniesz od testowania jednostkowego modelu favorites
. Co to jest test jednostkowy? Test jednostkowy polega na tym, że każda jednostka oprogramowania – czy to funkcja, obiekt czy widżet, poprawnie wykonuje swoje zadanie.
Wszystkie pliki testowe w aplikacji Flutter (oprócz testów integracji) znajdują się w katalogu test
.
Usuń: test/widget_test.dart
Zanim zaczniesz testowanie, usuń plik widget_test.dart
. Dodasz własne pliki testowe.
Utwórz nowy plik testowy
Najpierw przetestujesz metodę add()
w modelu Favorites
, aby sprawdzić, czy do listy został dodany nowy produkt i czy odzwierciedla ona tę zmianę. Zgodnie z konwencją struktura katalogów w katalogu test
imituje tę samą nazwę z dołączonym _test
w katalogu lib
i plikach Dart.
Utwórz katalog models
w katalogu test
. W nowym katalogu utwórz plik favorites_test.dart
o następującej zawartości:
test/models/favorites_test.dart
import 'package:test/test.dart';
import 'package:testing_app/models/favorites.dart';
void main() {
group('Testing App Provider', () {
var favorites = Favorites();
test('A new item should be added', () {
var number = 35;
favorites.add(number);
expect(favorites.items.contains(number), true);
});
});
}
Platforma testowa Flutter umożliwia powiązanie podobnych testów powiązanych ze sobą w grupie. W jednym pliku testowym może znajdować się wiele grup, które służą do testowania różnych części odpowiedniego pliku w katalogu lib
.
Metoda test()
przyjmuje 2 parametry pozycjonujące: description
testu i callback
, w którym faktycznie wpisujesz test.
Przetestuj usuwanie elementu z listy. Wstaw ten test w tej samej grupie Testing App Provider
:
test/models/favorites_test.dart
test('An item should be removed', () {
var number = 45;
favorites.add(number);
expect(favorites.items.contains(number), true);
favorites.remove(number);
expect(favorites.items.contains(number), false);
});
Testowanie
W wierszu poleceń przejdź do katalogu głównego projektu i wpisz to polecenie:
$ flutter test test/models/favorites_test.dart
Jeśli wszystko będzie działać prawidłowo, powinien wyświetlić się komunikat podobny do tego:
00:06 +2: All tests passed!
Pełny plik testowy: test/models/favorites_test.dart
.
Więcej informacji o testowaniu jednostkowym znajdziesz we wprowadzeniu do testowania jednostkowego.
6. Testowanie widżetów
W tym kroku dodasz kod testowania widżetów. Testowanie widżetów jest dostępne tylko w ramach Flutter, gdzie możesz testować każdy widżet w osobny sposób. W tym kroku testujesz poszczególne ekrany HomePage
i FavoritesPage
.
Testowanie widżetu używa funkcji testWidget()
zamiast funkcji test()
. Podobnie jak funkcja test()
, funkcja testWidget()
przyjmuje 2 parametry: description,
i callback
, jednak wywołanie zwrotne przyjmuje wartość WidgetTester
jako argument.
Testy widżetów korzystają z klasy TestFlutterWidgetsBinding
, która udostępnia do widżetów te same zasoby, które miałyby w uruchomionej aplikacji, np. informacje o rozmiarze ekranu i możliwości planowania animacji, ale bez uruchamiania aplikacji. Zamiast tego do utworzenia instancji widżetu używane jest środowisko wirtualne, a następnie przeprowadzanie testów wyników. W tym przypadku pumpWidget
rozpoczyna ten proces, prosząc platformę o podłączenie i pomiar określonego widżetu w taki sam sposób jak w aplikacji.
Platforma do testowania widżetów zapewnia narzędzia do znajdowania widżetów, np. text()
, byType()
i byIcon().
. Udostępnia też dopasowania, które służą do weryfikowania wyników.
Zacznij od przetestowania widżetu HomePage
.
Utwórz nowy plik testowy
Pierwszy test sprawdza, czy przewijanie elementu HomePage
działa prawidłowo.
Utwórz nowy plik w katalogu test
i nadaj mu nazwę home_test.dart
. W nowo utworzonym pliku dodaj ten kod:
test/home_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/home.dart';
Widget createHomeScreen() => ChangeNotifierProvider<Favorites>(
create: (context) => Favorites(),
child: const MaterialApp(
home: HomePage(),
),
);
void main() {
group('Home Page Widget Tests', () {
testWidgets('Testing Scrolling', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.text('Item 0'), findsOneWidget);
await tester.fling(
find.byType(ListView),
const Offset(0, -200),
3000,
);
await tester.pumpAndSettle();
expect(find.text('Item 0'), findsNothing);
});
});
}
Funkcja createHomeScreen()
służy do tworzenia aplikacji, która wczytuje widżet do przetestowania w MaterialApp, opakowany w zasadę ChangeNotifierProvider. Widżet strony głównej wymaga, aby oba te widżety znajdowały się nad nim w drzewie widżetów, aby mógł je dziedziczyć i uzyskiwać dostęp do oferowanych danych. Ta funkcja jest przekazywana jako parametr do funkcji pumpWidget()
.
Następnie sprawdź, czy platforma może znaleźć ListView
renderowany na ekranie.
Dodaj do home_test.dart
ten fragment kodu:
test/home_test.dart
group('Home Page Widget Tests', () {
// BEGINNING OF NEW CONTENT
testWidgets('Testing if ListView shows up', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.byType(ListView), findsOneWidget);
});
// END OF NEW CONTENT
testWidgets('Testing Scrolling', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.text('Item 0'), findsOneWidget);
await tester.fling(
find.byType(ListView),
const Offset(0, -200),
3000,
);
await tester.pumpAndSettle();
expect(find.text('Item 0'), findsNothing);
});
});
Testowanie
Najpierw uruchom test w taki sam sposób jak test jednostkowy, używając polecenia:
$ flutter test test/home_test.dart
Test powinien rozpocząć się szybko i powinno wyświetlić się taki komunikat:
00:02 +2: All tests passed!
Testy widżetów możesz też przeprowadzić na urządzeniu lub w emulatorze, aby obserwować przebieg testu. Umożliwia też ponowne uruchomienie urządzenia z pamięci.
Podłącz urządzenie lub uruchom emulator. Możesz też uruchomić test jako aplikację komputerową.
Za pomocą wiersza poleceń przejdź do katalogu głównego projektu i wpisz to polecenie:
$ flutter run test/home_test.dart
Konieczne może być wybranie urządzenia, na którym chcesz przeprowadzić test. W takim przypadku postępuj zgodnie z instrukcjami i wybierz urządzenie:
Multiple devices found:
Linux (desktop) • linux • linux-x64 • Ubuntu 22.04.1 LTS 5.15.0-58-generic
Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119
[1]: Linux (linux)
[2]: Chrome (chrome)
Please choose one (To quit, press "q/Q"):
Jeśli wszystko będzie działać, dane wyjściowe powinny wyglądać podobnie do tych:
Launching test/home_test.dart on Linux in debug mode...
Building Linux application...
flutter: 00:00 +0: Home Page Widget Tests Testing if ListView shows up
Syncing files to device Linux... 62ms
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
💪 Running with sound null safety 💪
An Observatory debugger and profiler on Linux is available at: http://127.0.0.1:35583/GCpdLBqf2UI=/
flutter: 00:00 +1: Home Page Widget Tests Testing Scrolling
The Flutter DevTools debugger and profiler on Linux is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:35583/GCpdLBqf2UI=/
flutter: 00:02 +2: All tests passed!
Następnie wprowadzasz zmiany w pliku testowym i naciskasz Shift + R
, aby ponownie uruchomić aplikację na gorąco i ponownie przeprowadzić wszystkie testy. Nie zatrzymuj aplikacji.
Dodaj więcej testów do grupy, która będzie testować widżety strony głównej. Skopiuj do pliku ten test:
test/home_test.dart
testWidgets('Testing IconButtons', (tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.byIcon(Icons.favorite), findsNothing);
await tester.tap(find.byIcon(Icons.favorite_border).first);
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Added to favorites.'), findsOneWidget);
expect(find.byIcon(Icons.favorite), findsWidgets);
await tester.tap(find.byIcon(Icons.favorite).first);
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Removed from favorites.'), findsOneWidget);
expect(find.byIcon(Icons.favorite), findsNothing);
});
Ten test sprawdza, czy kliknięcie przycisku IconButton
zmienia się z Icons.favorite_border
(otwarte serce) na Icons.favorite
(wypełnione serce), a następnie z powrotem na Icons.favorite_border
po ponownym dotknięciu.
Wpisz Shift + R
. Powoduje to ponowne uruchomienie aplikacji i przeprowadza wszystkie testy od nowa.
Pełny plik testowy: test/home_test.dart
.
Użyj tego samego procesu, aby przetestować FavoritesPage
z poniższym kodem. Wykonaj te same czynności i uruchom go.
test/ulubione_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';
late Favorites favoritesList;
Widget createFavoritesScreen() => ChangeNotifierProvider<Favorites>(
create: (context) {
favoritesList = Favorites();
return favoritesList;
},
child: const MaterialApp(
home: FavoritesPage(),
),
);
void addItems() {
for (var i = 0; i < 10; i += 2) {
favoritesList.add(i);
}
}
void main() {
group('Favorites Page Widget Tests', () {
testWidgets('Test if ListView shows up', (tester) async {
await tester.pumpWidget(createFavoritesScreen());
addItems();
await tester.pumpAndSettle();
expect(find.byType(ListView), findsOneWidget);
});
testWidgets('Testing Remove Button', (tester) async {
await tester.pumpWidget(createFavoritesScreen());
addItems();
await tester.pumpAndSettle();
var totalItems = tester.widgetList(find.byIcon(Icons.close)).length;
await tester.tap(find.byIcon(Icons.close).first);
await tester.pumpAndSettle();
expect(tester.widgetList(find.byIcon(Icons.close)).length,
lessThan(totalItems));
expect(find.text('Removed from favorites.'), findsOneWidget);
});
});
}
Ten test sprawdza, czy element znika po naciśnięciu przycisku zamykania (usuń).
Więcej informacji o testowaniu widżetów znajdziesz na stronie:
7. Testowanie interfejsu aplikacji za pomocą testów integracji
Testy integracji służą do sprawdzania, jak poszczególne elementy aplikacji współpracują ze sobą. Biblioteka integration_test
służy do testowania integracji w technologii Flutter. To jest wersja Selenium WebDriver, Protractor, Espresso lub Earl Gray. Pakiet używa wewnętrznie flutter_driver
do przeprowadzania testu na urządzeniu.
Pisanie testów integracji w Flutter przypomina pisanie testów widżetów z tą różnicą, że testy integracji są przeprowadzane na urządzeniach mobilnych, w przeglądarkach lub aplikacjach komputerowych, tzw. urządzeniach docelowych.
Pisanie testu
W katalogu głównym projektu utwórz katalog o nazwie integration_test
, a w nim utwórz nowy plik o nazwie app_test.dart
.
integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:testing_app/main.dart';
void main() {
group('Testing App', () {
testWidgets('Favorites operations test', (tester) async {
await tester.pumpWidget(const TestingApp());
final iconKeys = [
'icon_0',
'icon_1',
'icon_2',
];
for (var icon in iconKeys) {
await tester.tap(find.byKey(ValueKey(icon)));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Added to favorites.'), findsOneWidget);
}
await tester.tap(find.text('Favorites'));
await tester.pumpAndSettle();
final removeIconKeys = [
'remove_icon_0',
'remove_icon_1',
'remove_icon_2',
];
for (final iconKey in removeIconKeys) {
await tester.tap(find.byKey(ValueKey(iconKey)));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Removed from favorites.'), findsOneWidget);
}
});
});
}
Testowanie
Podłącz urządzenie lub uruchom emulator. Możesz też uruchomić test jako aplikację komputerową.
W wierszu poleceń przejdź do katalogu głównego projektu i wpisz to polecenie:
$ flutter test integration_test/app_test.dart
Jeśli wszystko działa, powinny wyświetlić się dane wyjściowe podobne do tych:
Multiple devices found:
Linux (desktop) • linux • linux-x64 • Ubuntu 22.04.1 LTS 5.15.0-58-generic
Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119
[1]: Linux (linux)
[2]: Chrome (chrome)
Please choose one (To quit, press "q/Q"): 1
00:00 +0: loading /home/miquel/tmp/testing_app/integration_test/app_test.dart B00:08 +0: loading /home/miquel/tmp/testing_app/integration_test/app_test.dart
00:26 +1: All tests passed!
8. Testowanie wydajności aplikacji za pomocą wtyczki Flutter Driver
Pisanie testu wydajności
W folderze integration_test utwórz nowy plik testowy o nazwie perf_test.dart z następującą zawartością:
integration_test/perf_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:testing_app/main.dart';
void main() {
group('Testing App Performance', () {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
testWidgets('Scrolling test', (tester) async {
await tester.pumpWidget(const TestingApp());
final listFinder = find.byType(ListView);
await binding.traceAction(() async {
await tester.fling(listFinder, const Offset(0, -500), 10000);
await tester.pumpAndSettle();
await tester.fling(listFinder, const Offset(0, 500), 10000);
await tester.pumpAndSettle();
}, reportKey: 'scrolling_summary');
});
});
}
Funkcja ensureInitialized()
sprawdza, czy sterownik testu integracji został zainicjowany, i w razie potrzeby ponownie go inicjuje. Ustawienie framePolicy
na fullyLive
jest dobrym rozwiązaniem do testowania animowanego kodu.
W ramach tego testu lista elementów jest przewijana bardzo szybko, a potem przewija się do końca. Funkcja traceAction()
rejestruje działania i generuje podsumowanie osi czasu.
Rejestrowanie wyników skuteczności
Aby zapisać wyniki, utwórz folder o nazwie test_driver
z plikiem o nazwie perf_driver.dart
i dodaj ten kod:
test_driver/perf_driver.dart
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() {
return integrationDriver(
responseDataCallback: (data) async {
if (data != null) {
final timeline = driver.Timeline.fromJson(
data['scrolling_summary'] as Map<String, dynamic>);
final summary = driver.TimelineSummary.summarize(timeline);
await summary.writeTimelineToFile(
'scrolling_summary',
pretty: true,
includeSummary: true,
);
}
},
);
}
Testowanie
Podłącz urządzenie lub uruchom emulator.
W wierszu poleceń przejdź do katalogu głównego projektu i wpisz to polecenie:
$ flutter drive \
--driver=test_driver/perf_driver.dart \
--target=integration_test/perf_test.dart \
--profile \
--no-dds
Jeśli wszystko działa, powinny wyświetlić się dane wyjściowe podobne do tych:
Running "flutter pub get" in testing_app...
Resolving dependencies...
archive 3.3.2 (3.3.6 available)
collection 1.17.0 (1.17.1 available)
js 0.6.5 (0.6.7 available)
matcher 0.12.13 (0.12.14 available)
meta 1.8.0 (1.9.0 available)
path 1.8.2 (1.8.3 available)
test 1.22.0 (1.23.0 available)
test_api 0.4.16 (0.4.18 available)
test_core 0.4.20 (0.4.23 available)
vm_service 9.4.0 (11.0.1 available)
webdriver 3.0.1 (3.0.2 available)
Got dependencies!
Running Gradle task 'assembleProfile'... 1,379ms
✓ Built build/app/outputs/flutter-apk/app-profile.apk (14.9MB).
Installing build/app/outputs/flutter-apk/app-profile.apk... 222ms
I/flutter ( 6125): 00:04 +1: Testing App Performance (tearDownAll)
I/flutter ( 6125): 00:04 +2: All tests passed!
All tests passed.
Gdy test się zakończy, katalog kompilacji w katalogu głównym projektu będzie zawierał 2 pliki:
scrolling_summary.timeline_summary.json
zawiera podsumowanie. Otwórz plik w dowolnym edytorze tekstu, aby sprawdzić zawarte w nim informacje.- Pole
scrolling_summary.timeline.json
zawiera pełne dane osi czasu.
Więcej informacji o testowaniu integracji znajdziesz na stronie:
9. Gratulacje!
Udało Ci się ukończyć ćwiczenia i poznać różne sposoby testowania aplikacji Flutter.
Czego się nauczyłeś?
- Jak sprawdzić dostawców za pomocą testów jednostkowych
- Jak testować widżety za pomocą platformy do testowania widżetów
- Jak przetestować interfejs aplikacji za pomocą testów integracji
- Jak sprawdzić wydajność aplikacji za pomocą testów integracji
Więcej informacji o testowaniu w Flutter znajdziesz na stronie