いるべです。 Flutterやってます。
Flutter
UI構築がとても簡単でいいですよね。ウンウン
この記事を読んでいる方は少なからずFlutterに関心があるんでしょう。ウンウン
今回はProvider使用時のWidget testについて簡単に書いていきます。
(え?Riv○rP○d?ちょっとよく分からないなぁ。)
画面数が増えるとテストがめんどい。
swiftで一画面単体のテストってあったんでしょうか? ((真上にめんどいって書いたくせにな
FlutterではWidget testを利用して作った画面単体でテストすることができます。
これで画面数が増えても気軽にテストすることができますね!
Widget test
state notifierを使っています。
freezed使った方がいいですがこれだけなので正直めんどくさかった。copyWith便利。
import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:state_notifier/state_notifier.dart';
import 'package:provider/provider.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StateNotifierProvider<ViewModel, State>(
create: (context) => ViewModel(),
child: Home(),
),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(context.watch<State>().one.toString()),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<ViewModel>().toggle(),
),
);
}
}
class ViewModel extends StateNotifier<State> {
ViewModel(): super(State(false));
void toggle() {
state = State(!state.one);
}
}
class State {
State(this.one);
bool one;
}
都合上、1ファイルに記述。
コードの命名などは二の次٩( ᐛ )و
大事なのは「stateを参照して、そのstateが持っているboolを、FloatingActionButtonをタップするたびにトグルするアプリ」であるということ
これのテストコードが下記
import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:state_notifier/state_notifier.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:disposable/main.dart' as home;
void main() {
testWidgets('Float Button Test', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: StateNotifierProvider<home.ViewModel, home.State>(
create: (context) => TestViewModel1(),
child: home.Home(),
),
));
final floatbtn = find.byWidgetPredicate((widget) => widget is FloatingActionButton);
expect(floatbtn, findsOneWidget);
expect(find.text('true'), findsOneWidget);
await tester.tap(floatbtn);
await tester.pump();
expect(find.text('false'), findsOneWidget);
});
}
class TestViewModel1 extends StateNotifier<home.State> implements home.ViewModel {
TestViewModel1(): super(home.State(true));
@override
void toggle() {
state = home.State(!state.one);
}
}
class TestViewModel2 extends home.ViewModel {
@override
void toggle() {
state = home.State(!state.one);
}
}
TestViewModelは2種類の形を取れる。試してないだけで他もやり方あるはず。
注意点は、
- testディレクトリからlibディレクトリのクラスを参照したい、使いまわしたいときはimportしたものをas homeなどで名前をつけてそこから参照する必要がある。
- Providerで利用するために指定する型は元のクラスを参照する必要がある。
StateNotifierProvider<TestViewModel, State>(create: ...);
ではFloatingActionButtonでViewModelを参照しておりTestViewModelを取得できないのでエラーになる。 - モックとして置き換えるクラスは、元になるクラスを継承しておく必要がある。(ここ要調査かも?)
tester.pump()
で画面を更新してくれる。これを入れなければ画面が更新されないため、falseを期待しているテストコードでこける。ここちょっとつまづいた。