当应用程序处于 Dark/Light 主题(特别是深色主题)时,在 Flutter 中测试应用程序外观的最佳方法

Best Approach To Testing App Appearance In Flutter While App Is In Dark/Light Theme (especially for Dark Theme)

经过 Flutter docs on testing, I have come to a point in my app where I would like to test both light and dark theme appearance on the app. Integration tests 之后可能是一个选项,但是它们对 运行 来说“昂贵”,我想将集成测试的考虑作为最后的手段测试 dark/light 主题应用外观。

这是我试过的 (Widget Tests) testWidgets:

void main() {
  testWidgets("Test that the app renders properly in 'Dark Theme'.",
      (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.dark(),
        home: RegistrationHomePage(),
      ),
    );

    expect(
      SchedulerBinding.instance.window.platformBrightness,
      Brightness.dark,
      reason: "The test suite should now be testing with app theme set to dark theme.",
    );
  })
}

但是,这个测试失败了。它失败了,因为 Widget Test 仍在浅色主题而不是深色主题中执行此测试.

更新: 实际上,我正在测试文本小部件在 [浅色主题和深色主题] 中显示的颜色。

main.dart

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      debugShowCheckedModeBanner: false,
      themeMode: ThemeMode.system,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: RegistrationHomePage(),
    );
  }
}

registration_screen.dart

class RegistrationHomePage extends StatefulWidget {
  // Constructor
  RegistrationHomePage({Key key}) : super(key: key);

  @override
  _RegistrationHomePageState createState() => _RegistrationHomePageState();
}

class _RegistrationHomePageState extends State<RegistrationHomePage> {
  void setState(fn) {
    super.setState(fn);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(
          "Welcome to the app",
          style: TextStyle(
            color: MediaQuery.of(context).platformBrightness == Brightness.dark
                ? Colors.green.shade900
                : Colors.green,
            fontWeight: FontWeight.w600,
            fontSize: 35.0,
          ),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

test.dart

void main() {
  testWidgets("Test that the app renders properly in 'Dark Theme'.",
      (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.dark(),
        home: RegistrationHomePage(),
      ),
    );

    final Finder loginTextFinder = find.text("Welcome to the app.");
    final Text loginText = tester.firstWidget(loginTextFinder);

    expect(
      WelcomeText.style.color,
      Colors.green.shade900,
      reason:
          'While the system dark is dark theme, the text color should be dark green',
    );
  });
}

测试失败。测试失败,因为测试是在应用程序设置为浅色主题而不是深色主题的情况下进行的。一定有什么我在这里做错了。现在看来我无法将测试应用程序设置为我在 test.dart

中尝试过的深色主题
void main() {
  testWidgets("Test that the app renders properly in 'Dark Theme'.",
      (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.dark(),
        home: RegistrationHomePage(),
      ),
    );
  });
}

如果我是 运行 深色主题应用程序的小部件测试,这就是我会选择的路线:

void main() {
  testWidgets("Test that the app renders properly in dark theme",
      (WidgetTester tester) async {
    await tester.pumpWidget(MaterialApp(
      theme: ThemeData(brightness: Brightness.dark),
      home: RegistrationHomePage(),
    ));

    // Capture a BuildContext object
    final BuildContext context = tester.element(find.byType(MaterialApp));

    // Get finders and relevant objects that would be available at first load of MaterialApp()
    final Finder welcomeTextFinder = find.text("Welcome to the app");
    final Text welcomeText = tester.firstWidget(welcomeTextFinder);

    // functions
    bool testIsInLightTheme() {
      /// return true if the test is in light theme, else false
      if (Theme.of(context).brightness == Brightness.light) {
        return true;
      }
      return false;
    }

    // Here is just a test to confirm that the MaterialApp is now in dark theme
    expect(
      Theme.of(tester.element(find.byWidget(welcomeText))).brightness,
      equals(Brightness.dark),
      reason:
          "Since MaterialApp() was set to dark theme when it was built at tester.pumpWidget(), the MaterialApp should be in dark theme",
    );

    // Now let's test the color of the text
    expect(
      welcomeText.style.color,
      testIsInLightTheme() ? Colors.green : Colors.green.shade900,
      reason:
          "When MaterialApp is in light theme, text is black. When Material App is in dark theme, text is white",
    );
  });
}

一些值得注意的提及: 在测试中捕获 BuildContext 对象:

标准指令(基本上是一些flutter source code that dealt with theme testing) on how theme testing should be approached [By the Flutter team that contributed to theme related testing!]

这是我的简短解决方案,基于@Alvindera97 的回答:

  Future<void> testDarkMode(
    WidgetTester tester, {
    required keyElementToCheckDarkmodeIn,
    required keyDarkmodeSwitcher,
  }) async {
    expect(
      Theme.of(tester.element(_finder.key(keyElementToCheckDarkmodeIn))).brightness,
      equals(Brightness.light),
    );

    await tester.tap(_finder.key(keyDarkmodeSwitcher));
    await tester.pump();
    await tester.pumpAndSettle(_testUtils.delay(DELAY));

    expect(
      Theme.of(tester.element(_finder.key(keyElementToCheckDarkmodeIn))).brightness,
      equals(Brightness.dark),
    );
  }

  Finder key(String keyText) {
    return find.byKey(Key(keyText));
  }