テストtips - TetsuFe/state_notifier_sample6 GitHub Wiki
StateNotifier.stateはprotectedなのですが、debugStateというプロパティもあり、テストをするときにはこれを利用します。
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_state_management/story_state_notifier.dart';
import 'package:flutter_state_management/story.dart';
void main() {
test('StoryStateNotifier test', () {
final story = Story(
id: 1,
title: 'テスト',
summary: 'テストです',
thumbnailImagePath: 'assets/story/1/hokuma.jpg',
isRead: false);
final storyStateNotifier = StoryStateNotifier(story);
storyStateNotifier.markAsRead();
expect(
storyStateNotifier.debugState,
story.copyWith(isRead: true),
);
});
}
CheckBoxの値を取得するなどの際に、Widgetが欲しい時がある
参考: https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/checkbox_test.dart
expect(tester.widget<Checkbox>(find.byType(Checkbox)).value, false);
- pushは普通にボタンを押す操作などをすればOK
- その後、await test.pumpAndSettle();
- popはtester.pageBack()を使う
https://stackoverflow.com/questions/50704647/how-to-test-navigation-via-navigator-in-flutter
Image.networkはHttpClientがモックであるため、400や404になるらしい
参考: https://qiita.com/kasa_le/items/9ab139f11bce1ce24235
pubspec.yaml
dev_dependencies:
image_test_utils: ^1.0.0
widget_test.dart
import 'package:image_test_utils/image_test_utils.dart';
void main() {
testWidgets('image.network test',
(WidgetTester tester) async {
provideMockedNetworkImages(() async {
// 普通にテストできる
import 'package:flutter_state_management/user_question_chat/models/user_question_chat_api.dart';
import 'package:flutter_state_management/user_question_chat/widgets/user_question_chat.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
class MockUserQuestionChatApi extends Mock implements UserQuestionChatApi {}
void main() {
testWidgets('chat list displaying widget test', (tester) async {
tester.pumpWidget(
# ここでテストしたいコードに出てくる型を指定
Provider<UserQuestionChatApi>(
child: MessageListView(),
# 実際に注入するのは Mock
create: (context) => MockUserQuestionChatApi(),
),
);
});
}
https://stackoverflow.com/questions/53676526/textdirection-null-assert-is-not-null-listtile
https://github.com/flutter/flutter/issues/15245#issuecomment-537675925
/// A fake implementation of AuthServices
class FakeAuthService implements AuthService {
Stream<User> get currentUser => Stream.fromIterable([User.fake()]);
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_state_management/user_question_chat/models/message.dart';
import 'package:flutter_state_management/user_question_chat/models/user_question_chat_api.dart';
import 'package:flutter_state_management/user_question_chat/widgets/user_question_chat.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
class MockUserQuestionChatApi extends Mock implements UserQuestionChatApi {
MockUserQuestionChatApi({this.allMessageSnapshotData});
final List<Message> allMessageSnapshotData;
@override
Stream<List<Message>> allMessageSnapshot() =>
Stream.value(allMessageSnapshotData);
}
void main() {
testWidgets('chat list displaying widget test', (tester) async {
final message = Message(
username: 'testuser',
body: 'こんにちは',
createdDate: DateTime(2020, 1, 1),
);
await tester.pumpWidget(
MaterialApp(
home: Provider<UserQuestionChatApi>(
child: Scaffold(body: Column(children: [MessageListView()])),
create: (context) =>
MockUserQuestionChatApi(allMessageSnapshotData: [message]),
)),
);
await tester.pump(Duration.zero);
expect(find.byType(MessageListView), findsOneWidget);
expect(find.byType(ListView), findsOneWidget);
expect(find.byType(MessageBubble), findsOneWidget);
expect(find.text(message.username), findsOneWidget);
expect(find.text(message.body), findsOneWidget);
expect(find.text(message.createdDate.toIso8601String()), findsOneWidget);
});
}
Future eventFiring(WidgetTester tester) async { await tester.pump(Duration.zero); }
testWidgets('StreamBuilder', (WidgetTester tester) async { await tester.pumpWidget(StreamBuilder( stream: Stream.fromIterable(['hello', 'world']), builder: snapshotText, )); await eventFiring(tester); });
// tester.pump(Duration.zero);
// tester.pumpAndSettle();
の場合、
00:02 +0: /Users/tetsu/flworks/flutter_novel/test/chat_test.dart: chat list displaying widget test
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _WidgetTypeFinder:<zero widgets with type "ListView" (ignoring offstage widgets)>
Which: means none were found but one was expected
Assertion failed: file:///private/var/folders/_z/ktrjffx13y5fygdgd6hs1h6w0000gn/T/scratch_spaceURK5zR/packages/flutter_test/src/test_async_utils.dart:312:14 lineMatch != null is not true
Assertion failed: file:///private/var/folders/_z/ktrjffx13y5fygdgd6hs1h6w0000gn/T/scratch_spaceURK5zR/packages/flutter_test/src/test_async_utils.dart:312:14 lineMatch != null is not true
Assertion failed: file:///private/var/folders/_z/ktrjffx13y5fygdgd6hs1h6w0000gn/T/scratch_spaceURK5zR/packages/flutter_test/src/widget_tester.dart:543:12 duration > Duration.zero is not true
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ The following TestFailure object was thrown running a test: Expected: exactly one matching node in the widget tree Actual: _WidgetTypeFinder:<zero widgets with type "MessageBubble" (ignoring offstage widgets)> Which: means none were found but one was expected
StreamBuilderのListViewの中身が表示されない
class MessageListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Provider.of<UserQuestionChatApi>(context, listen: false)
.allMessageSnapshot(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('エラーが発生しています');
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final messages = snapshot.data as List<Message>;
print('********************');
print(messages[0]);
return ListView.builder(
reverse: true,
itemCount: messages.length,
shrinkWrap: true,
primary: false,
itemBuilder: (context, int index) {
return MessageBubble(message: messages[index]);
});
},
);
}
}
00:02 +0: /Users/tetsu/flworks/flutter_novel/test/chat_test.dart: chat list displaying widget test
Message(username: testuser, body: こんにちは, createdDate: 2020-01-01 00:00:00.000)
どうやら、とれてはいるものの表示される前に評価されている?あるいは、表示されていない
ListViewの要素が見えていないだけで、ドラッグすればいいのではないか説 https://stackoverflow.com/questions/54651878/how-to-find-off-screen-listview-child-in-widget-tests
https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/list_view_test.dart#L94
await tester.pumpWidget(
Provider<UserQuestionChatApi>(
child: Directionality(
textDirection: TextDirection.ltr, child: MessageListView()),
create: (context) =>
MockUserQuestionChatApi(allMessageSnapshotData: [message]),
),
);
════════════════════════════════════════════════════════════════════════════════════════════════════ ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ The following TestFailure object was thrown running a test: Expected: exactly one matching node in the widget tree Actual: _TextFinder:<zero widgets with text "testuser" (ignoring offstage widgets)> Which: means none were found but one was expected
00:01 +0: loading /Users/tetsu/flworks/flutter_novel/test/chat_test.dart ERROR - 2020-06-25 12:10:24.491160
GET /packages/ui/assets/Roboto-Regular.ttf
Error thrown by handler.
NoSuchMethodError: The method 'toFilePath' was called on null.
Receiver: null
Tried calling: toFilePath()
dart:core Object.noSuchMethod
package:flutter_tools/src/test/flutter_web_platform.dart 225:48 FlutterWebPlatform._packageFilesHandler
===== asynchronous gap ===========================
package:shelf/shelf_io.dart 63:34 serveRequests.<fn>.<fn>
===== asynchronous gap ===========================
package:shelf/src/io_server.dart 53:5 IOServer.mount
package:flutter_tools/src/test/flutter_web_platform.dart 74:13 new FlutterWebPlatform._
package:flutter_tools/src/test/flutter_web_platform.dart 90:31 FlutterWebPlatform.start
dart:async _completeOnAsyncReturn
package:http_multi_server/http_multi_server.dart HttpMultiServer._loopback
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
package:flutter_tools/src/test/runner.dart 142:37 _FlutterTestRunnerImpl.runTests.<fn>
dart:async new Future.sync
package:async/src/async_memoizer.dart 43:45 AsyncMemoizer.runOnce
package:test_core/src/runner/loader.dart 230:28 Loader.loadFile.<fn>
package:test_core/src/runner/load_suite.dart 98:31 new LoadSuite.<fn>.<fn>
package:test_core/src/runner/load_suite.dart 108:8 new LoadSuite.<fn>
package:test_api/src/backend/invoker.dart 400:30 Invoker._onRun.<fn>.<fn>.<fn>.<fn>
===== asynchronous gap ===========================
dart:async new Future
package:test_api/src/backend/invoker.dart 399:21 Invoker._onRun.<fn>.<fn>.<fn>
dart:async runZoned
package:test_api/src/backend/invoker.dart 387:9 Invoker._onRun.<fn>.<fn>
dart:async runZoned
package:test_api/src/backend/invoker.dart 148:7 Invoker.guard
package:test_api/src/backend/invoker.dart 436:15 Invoker._guardIfGuarded
package:test_api/src/backend/invoker.dart 386:7 Invoker._onRun.<fn>
package:stack_trace Chain.capture
package:test_api/src/backend/invoker.dart 385:11 Invoker._onRun
package:test_api/src/backend/live_test_controller.dart 152:11 LiveTestController.run
00:03 +1: loading /Users/tetsu/flworks/flutter_novel/test/story_test.dart ERROR - 2020-06-25 12:10:26.675708
GET /packages/ui/assets/Roboto-Regular.ttf
Error thrown by handler.
NoSuchMethodError: The method 'toFilePath' was called on null.
Receiver: null
Tried calling: toFilePath()
dart:core Object.noSuchMethod
package:flutter_tools/src/test/flutter_web_platform.dart 225:48 FlutterWebPlatform._packageFilesHandler
===== asynchronous gap ===========================
package:shelf/shelf_io.dart 63:34 serveRequests.<fn>.<fn>
===== asynchronous gap ===========================
package:shelf/src/io_server.dart 53:5 IOServer.mount
package:flutter_tools/src/test/flutter_web_platform.dart 74:13 new FlutterWebPlatform._
package:flutter_tools/src/test/flutter_web_platform.dart 90:31 FlutterWebPlatform.start
dart:async _completeOnAsyncReturn
package:http_multi_server/http_multi_server.dart HttpMultiServer._loopback
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
package:flutter_tools/src/test/runner.dart 142:37 _FlutterTestRunnerImpl.runTests.<fn>
dart:async new Future.sync
package:async/src/async_memoizer.dart 43:45 AsyncMemoizer.runOnce
package:test_core/src/runner/loader.dart 230:28 Loader.loadFile.<fn>
package:test_core/src/runner/load_suite.dart 98:31 new LoadSuite.<fn>.<fn>
package:test_core/src/runner/load_suite.dart 108:8 new LoadSuite.<fn>
package:test_api/src/backend/invoker.dart 400:30 Invoker._onRun.<fn>.<fn>.<fn>.<fn>
===== asynchronous gap ===========================
dart:async new Future
package:test_api/src/backend/invoker.dart 399:21 Invoker._onRun.<fn>.<fn>.<fn>
dart:async runZoned
package:test_api/src/backend/invoker.dart 387:9 Invoker._onRun.<fn>.<fn>
dart:async runZoned
package:test_api/src/backend/invoker.dart 148:7 Invoker.guard
package:test_api/src/backend/invoker.dart 436:15 Invoker._guardIfGuarded
package:test_api/src/backend/invoker.dart 386:7 Invoker._onRun.<fn>
package:stack_trace Chain.capture
package:test_api/src/backend/invoker.dart 385:11 Invoker._onRun
package:test_api/src/backend/live_test_controller.dart 152:11 LiveTestController.run
00:04 +2: All tests passed!