プラットフォーム(OS)によって遷移先の画面を変える機能のウィジェットテストを書いていた際
日本語の情報が見つからずちょっとした躓きポイントにハマってしまったのでその対処の記録です。
プラットフォームにより異なる挙動を一つの環境でテストするには、テスト実行時プラットフォームを偽装する必要があります。
またそれだけでなく、プロダクトコード側のプラットフォーム判別もテスト時の偽装に対応したものでなければならない:
具体的には dart:io
の Platform.isXxx
は偽装に適応する仕組みがないため使えず、flutter/foundation
の defaultTargetPlatform
でないといけないのですが
その辺りまではテスト時の具体的な偽装のやり方共々 下記記事に一通り書かれているのでそちらをご覧下さい。
debugDefaultTargetPlatformOverride
に TargetPlatform.xxx
をセットしてプラットフォーム偽装を行った際は
上記記事内にもあるようにそのテストから抜ける際に必ず debugDefaultTargetPlatformOverride
に null
を(再)セットする必要があり、忘れると下記のようなエラーでテストが失敗します。
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
The value of a foundation debug variable was changed by the test.
When the exception was thrown, this was the stack:
#0 debugAssertAllFoundationVarsUnset.<anonymous closure> (package:flutter/src/foundation/debug.dart:41:7)
#1 debugAssertAllFoundationVarsUnset (package:flutter/src/foundation/debug.dart:44:4)
#2 TestWidgetsFlutterBinding._verifyInvariants (package:flutter_test/src/binding.dart:1068:12)
#3 AutomatedTestWidgetsFlutterBinding._verifyInvariants (package:flutter_test/src/binding.dart:1525:11)
#4 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:1049:7)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
The test description was:
Your Test Case Name
════════════════════════════════════════════════════════════════════════════════════════════════════
ならば tearDown()
でnullをセットすればいいよね、上記の記事にもそう書かれているしということで
tearDown(() {
debugDefaultTargetPlatformOverride = null;
});
これでOKっしょ・・・と思ったのですがテスト結果変わりません。エラー内容も同じ。
えぇ? なんでナンデ?? と情報探し回ったりコード弄りまわしてみたりすることしばし、見つかった英語の情報から得た結論は
ウィジェットテストの場合 tearDown()
でnullセットしてはならず、各テストの末尾にnullセット処理を都度書かないといけない でした。
回答末尾の Important: 以下をざっくり訳すと
debugDefaultTargetPlatformOverride = null
をtearDown()
に移したくなるだろうけどそれだと動かない
何故ならdebugAssertAllFoundationVarsUnset()
はtearDown()
より前に呼ばれるから
という感じでしょうか(若干意訳)。直前の節で
debugDefaultTargetPlatformOverride はテスト全体に影響するトップレベル変数なので各テスト終了時には null にリセットされていなければならないので
debugAssertAllFoundationVarsUnset()
でそのチェックを行う
とも書かれている(同)ので、ウィジェットテストでは tearDown()
より前に expect(debugDefaultTargetPlatformOverride, isNull)
みたいな検証がされるんだな~と理解しておけばよさそうに思いました。