Help us understand the problem. What is going on with this article?

Flutter/WidgetTestでUnsupported operation: Cannot set value in unmodifiable Mapエラーが出る

環境

訳あって最新版を使っていません(macをCatalinaにアップデートできない==Xcodeの最新版を入れられないので)。
従って、バージョン違いによる不動作などあるかも知れません。お気づきの点があったら是非コメント下さい。

$ flutter --version
Flutter 1.12.13+hotfix.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 27321ebbad (4 months ago) • 2019-12-10 18:15:01 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0

現象

WidgetTestで、pumpWidget(MyApp())しているテストを、他のテストと連続実行したときのみ、以下のエラーが出る。
単独実行したときは、出ない。

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following UnsupportedError was thrown building MediaQuery(MediaQueryData(size: Size(800.0,
600.0), devicePixelRatio: 3.0, textScaleFactor: 1.0, platformBrightness: Brightness.light, padding:
EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, physicalDepth:
1.7976931348623157e+308, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast:
false,disableAnimations: false, invertColors: false, boldText: false)):
Unsupported operation: Cannot set value in unmodifiable Map

原因

どうやら、ローカライゼーション用の設定が悪さをしているようです。
Flutterをそのまま実行すると、日本語フォントがおかしくありませんか?それを解消するため、MaterialAppに次のように指定していました。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
   return MaterialApp(
      // 日本語フォント設定
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja', ''), // Japanese
      ],
     ....

これの設定を無くすと、大丈夫でした。

このテストの前に実行するテストでは、

return MaterialApp(
      home: HomePage(),
    );

としてダミーのMaterialAppを起動していて、どうやらそのクラスはテストの間で再作成していないらしく、MyAppの設定でImuutableなMapを上書きしようとして怒られているようです。
groupを変えてみても同じでした。

解決策

1.MyAppを起動するテストを先に持ってくる

未設定のプロパティは、特に値が上書きされることはないようなので、

MyApp→ダミーMaterialApp

の順の起動になれば、エラーが起きなくなります。
なので、テストの順番をそのように変えます。
JUnitと違い、Dartのテストは書いた順番に実行されますから、メソッドをカット&ペースとして動かすだけです。

2.TestAppクラスを作ってローカライゼーション設定を入れる

ダミーのMaterialAppを作っていたのを、TestAppクラスとして次のようにします。

class TestApp extends StatelessWidget {
  TestApp(this.widget);

  final Widget widget;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // 日本語フォント設定
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja', ''), // Japanese
      ],
      home: widget,
    );
  }
}

///テストコード
await tester.pumpWidget(TestApp(HomePage()));

でも、テスト時にフォントは関係ないしなあ・・・
ということで、悩ましいですが、いったん1の対応を取っています。

なお、テストファイルが違う場合は、作り直されるみたいです。というかファイル毎に別スレッドでテストしているようなログの出方をしていますので、当然ながらファイルが違うテストは全部別インスタンスでしょうね。

というかFlutterのテストレポート見づらい・・・

kasa_le
言語経験はC→C++→Java+Android(たまにiOS/swift経験なし)→Kotlin Flutterも良いよ. 独学でPHPとpython
Leading-Edge
ITエンジニアの生涯価値向上を目指し、派遣・紹介・教育・自社開発など様々な分野から全方位支援を行っております。
https://www.leadinge.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away