0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlutterでWidget testをする。(with Provider)

Posted at

いるべです。 Flutterやってます。

Flutter

UI構築がとても簡単でいいですよね。ウンウン
この記事を読んでいる方は少なからずFlutterに関心があるんでしょう。ウンウン

今回はProvider使用時のWidget testについて簡単に書いていきます。
(え?Riv○rP○d?ちょっとよく分からないなぁ。)

画面数が増えるとテストがめんどい。

swiftで一画面単体のテストってあったんでしょうか? ((真上にめんどいって書いたくせにな

FlutterではWidget testを利用して作った画面単体でテストすることができます。

これで画面数が増えても気軽にテストすることができますね!

Widget test

state notifierを使っています。
freezed使った方がいいですがこれだけなので正直めんどくさかった。copyWith便利。

lib/main.dart
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をタップするたびにトグルするアプリ」であるということ

これのテストコードが下記

test/widget_test.dart
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を期待しているテストコードでこける。ここちょっとつまづいた。

(RiverPodでもやりたい)

ご愛読ありがとうございました。いるべ先生の次回作にご期待ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?