Flutter Widget 测试无法正确模拟不同的屏幕尺寸

TSR*_*TSR 6 testing unit-testing dart flutter

在部署我的 Flutter 应用程序之前,我想在多个屏幕尺寸上对其进行测试,以检查是否有Renderflex overflow适用于较小屏幕的应用程序。

但是当我在小部件测试期间第一次修改屏幕尺寸以匹配我在开发期间使用的设备时,我意识到小部件测试已经抛出Render overflow错误,即使它在真实设备上没有这样的错误。所以我问了这个问题How to fix A RenderFlex overflowed during Widget Test

但我在进一步调查并使用 Flutter 黄金功能测试后,将 png 从小部件测试中提取出来,我将问题缩小到文本大小的差异。

您可以在可重复步骤清楚地看到下面的窗口小部件文本中的文字是更强壮(右侧)比在真实设备的实际文本(左侧)。

在此处输入图片说明

Widget 测试期间较大的文本大小会导致RenderFlex error我的应用程序中出现 。

重现步骤:

  1. 现在连接一个真实的设备并运行此代码 flutter run

lib/main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: TextScaleComparaison(),
    ),
  );
}

class TextScaleComparaison extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final widget = Scaffold(
      body: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          final width = MediaQuery.of(context).size.width;
          final height = MediaQuery.of(context).size.height;
          final dpr = MediaQuery.of(context).devicePixelRatio;
          final textScale = MediaQuery.of(context).textScaleFactor;
          final vi = MediaQuery.of(context).viewInsets;
          final vip = MediaQuery.of(context).viewPadding;
          final font = DefaultTextStyle.of(context).style.fontFamily;
          print("width is $width and height is $height and dpi is $dpr txtScale is $textScale vi is $vi vip is $vip font is $font");
          return Center(child: Text("This cannot be that long!!"));
        },
      ),
    );
    return widget;
  }
}
Run Code Online (Sandbox Code Playgroud)
  1. 检查日志,您应该会看到设备屏幕信息:

对我来说,我得到了:

I/flutter (27450): width is 411.42857142857144 and height is 797.7142857142857 and dpi is 2.625 txtScale is 1.1 vi is EdgeInsets.zero vip is EdgeInsets(0.0, 24.0, 0.0, 0.0) font is Roboto

屏幕复制widthheighttextScaledevicePixelRatio在下面的代码中的下一个步骤。

  1. 编辑下面的代码以添加上面的设置,因为我们想在测试中模拟这个精确的屏幕尺寸。

test/test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {

  testWidgets(
    "Emulate real screen size",
    (WidgetTester tester) async {
      // Adjust these to match your actual device screen specs
      final width = 414;
      final height = 846;
      tester.binding.window.devicePixelRatioTestValue = (2.625);
      tester.binding.window.textScaleFactorTestValue = (1.1);
      final dpi = tester.binding.window.devicePixelRatio;
      tester.binding.window.physicalSizeTestValue = Size(width * dpi, height * dpi);
      await tester.pumpWidget(
        MediaQuery(
          data: MediaQueryData(),
          child: MaterialApp(
            home: TextScaleComparaison(),
          ),
        ),
      );
      await expectLater(
        find.byType(TextScaleComparaison),
        matchesGoldenFile("text.png"),
      );
    },
  );
}
Run Code Online (Sandbox Code Playgroud)

运行test.dartflutter test --update-goldens test/test.dart

这将在以下位置创建一个 png 文件 test/text.png

检查日志:对我来说它打印:

width is 414.0 and height is 846.0 and dpi is 2.625 txtScale is 1.1 vi is EdgeInsets.zero vip is EdgeInsets.zero font is Roboto

我缺少什么?为什么文字显示不能和真机一模一样?

TSR*_*TSR 10

这是因为所使用的字体差异的flutter testflutter run

Roboto如果您没有更改其他字体,则Flutter 的默认字体适用于 Android。

  1. 默认 Android:Roboto字体和 iOS:San Francisco字体
  2. 自定义https://flutter.dev/docs/cookbook/design/fonts

1) 或 2) 这些字体flutter test默认不可用。Flutter 测试故意使用一种名为的字体Ahem,该字体由您在屏幕截图中看到的方形块组成。

这是预览:

在此处输入图片说明

Ahem字体方块比您使用的正常字体要大得多。因此,它导致RenderFlex overflow error

解决方案

要在您的设备中实现近乎完美的模拟,您flutter test必须下载字体数据,然后加载您正在使用的确切字体。

要在小部件测试中加载字体,您应该在testWidgets函数内部执行或setUp

final flamante = rootBundle.load('assets/fonts/Flamante-Roma-Medium.ttf');
      await FontLoader('FlamanteRoma')
        ..addFont(flamante)
        ..load();
Run Code Online (Sandbox Code Playgroud)

然后将此字体添加到ThemeData抽小部件之前。

 theme: ThemeData(
              fontFamily: 'FlamanteRoma',
 ),
Run Code Online (Sandbox Code Playgroud)

最终的 test.dart 代码是:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:example/test/compare_test_size.dart';

void main() {
  testWidgets(
    "Emulate real screen size",
    (WidgetTester tester) async {
      final flamante = rootBundle.load('assets/fonts/Flamante-Roma-Medium.ttf');
      await FontLoader('FlamanteRoma')
        ..addFont(flamante)
        ..load();

      // Adjust these to match your actual device screen specs
      final width = 411.4;
      final height = 797.7;
      tester.binding.window.devicePixelRatioTestValue = (2.625);
      tester.binding.window.textScaleFactorTestValue = (1.1);
      final dpi = tester.binding.window.devicePixelRatio;
      tester.binding.window.physicalSizeTestValue = Size(width * dpi, height * dpi);
      await tester.pumpWidget(
        MediaQuery(
          data: MediaQueryData(),
          child: MaterialApp(
            home: TextScaleComparaison(),
            theme: ThemeData(
              fontFamily: 'FlamanteRoma',
            ),
          ),
        ),
      );
      await expectLater(
        find.byType(TextScaleComparaison),
        matchesGoldenFile("text.png"),
      );
    },
  );
}
Run Code Online (Sandbox Code Playgroud)

现在重新生成黄金测试并检查png。您将看到真正的文本,而不是框:

test/test.png

在此处输入图片说明

并且不要忘记在 main.dart 中添加相同的字体

runApp(
    MaterialApp(
      home: TextScaleComparaison(),
      theme: ThemeData(
        fontFamily: 'FlamanteRoma',
      ),
    ),
  );
Run Code Online (Sandbox Code Playgroud)

并且不要忘记更新pubspec.yaml和运行flutter pub get

- family: FlamanteRoma
  fonts:
    - asset: assets/fonts/Flamante-Roma-Medium.ttf
Run Code Online (Sandbox Code Playgroud)

  • 作为一个快速破解,您可以更改字体缩放来克服由测试字体引起的更常见的 RenderFlex 错误,例如“tester.binding.window.textScaleFactorTestValue = (0.8);” (2认同)
  • 我无法让它在 Flutter 1.17.5(也不是 1.17.4,也不是 1.20.0-7.3.pre-beta)上工作。我可以执行“flutter build bundle”,如果我将“pubspec.yaml”更改为不存在的路径,它会失败,所以我确信该部分是正确的。运行“flutter test”失败,并显示“无法加载资源:assets/fonts/Flamante-Roma-Medium.ttf”。这是在`PlatformAssetBundle.load`中抛出的,我认为这就是问题所在,它不应该是PlatformAssetBundle?我还有什么需要做的吗? (2认同)