【问题标题】:Flutter Test with easy_localization and big translation json file使用 easy_localization 和大翻译 json 文件进行 Flutter 测试
【发布时间】:2021-09-30 11:46:17
【问题描述】:

我在颤振项目中使用easy_localization。 我需要编写一些小部件测试。

但看起来当带有翻译的.json 文件太大时,MaterialApp 永远不会构建它的child,因此我无法测试我的小部件。

这是我的小型可重现项目的架构:

my_project
 |- assets
 |   |- lang
 |   |   |- ru.json
 |   |   |- sv.json
 |- test
 |   |- test_test.dart

这是我的test_test.dart 文件:

import 'package:easy_localization/easy_localization.dart';
import 'package:easy_logger/easy_logger.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  testWidgets('', (tester) async {
    await tester.runAsync(() async {
      SharedPreferences.setMockInitialValues({});
      EasyLocalization.logger.enableLevels = <LevelMessages>[
        LevelMessages.error,
        LevelMessages.warning,
      ];
      await EasyLocalization.ensureInitialized();
      await tester.pumpWidget(
        EasyLocalization(
          supportedLocales: const [Locale('sv')],  // <- Change it to 'ru' and it doesn't work
          path: 'assets/lang',
          child: Builder(
            builder: (context) {
              print('builder1');
              return MaterialApp(
                locale: EasyLocalization.of(context).locale,
                supportedLocales: EasyLocalization.of(context).supportedLocales,
                localizationsDelegates: EasyLocalization.of(context).delegates,
                home: Builder(
                  builder: (context) {
                    print('builder2');
                    return const SizedBox.shrink();
                  },
                ),
              );
            },
          ),
        ),
      );
      await tester.pumpAndSettle();
    });
  });
}

sv.json(一个小文件):

{
  "0": "0"
}

ru.json(一个大文件):

{
  "0": "0 - xx ... xx",  // <- 1000 "x"
  "1": "1 - xx ... xx",  // <- 1000 "x"
  // ...
  "3998": "3998 - xx ... xx",  // <- 1000 "x"
  "3999": "3999 - xx ... xx"  // <- 1000 "x"
}

在我的测试中,我有 2 个 prints 应该分别打印 builder1builder2

当我在测试中使用Locale('sv') 时效果很好:

00:02 +0:                                                                                                                                                                                                                                               
builder1
builder2
00:02 +1: All tests passed!

但是当我使用Locale('ru') 时,MaterialApp 不会生成它的孩子,我也没有得到打印builder2

00:03 +0:                                                                                                                                                                                                                                               
builder1
00:03 +1: All tests passed!

我该如何解决这个问题?

【问题讨论】:

标签: flutter localization flutter-test flutter-localizations


【解决方案1】:

当您使用 CodegenLoader 时,这是一个小方法,您可以使用它来设置简单的本地化单例,类似于 Valentin Vignal 的 json 示例,我还建议使用不带回调参数的小函数,而不是嵌套设置函数,在至少如果您没有对回调本身进行操作。通过这种方式,很明显(只是!)副作用正在发生。

void addLocalization() {
  Localization.load(
    const Locale('en'),
    translations: Translations(CodegenLoader.en),
  );
}

【讨论】:

    【解决方案2】:

    最后,我通过添加一个文件test/flutter_test_config.dart 来修复它。我的灵感来自this issue

    import 'dart:async';
    import 'dart:convert';
    import 'dart:io';
    import 'package:easy_localization/src/localization.dart';
    import 'package:easy_localization/src/translations.dart';
    import 'package:flutter/widgets.dart';
    
    Future<void> testExecutable(FutureOr<void> Function() testMain) async {
      final content = await File('assets/lang/sv.json').readAsString(); // <- Or `ru.json`
      final data = jsonDecode(content) as Map<String, dynamic>;
    
      // easy_localization works with a singleton instance internally. We abuse
      // this fact in tests and just let it load the English translations.
      // Therefore we don't need to deal with any wrapper widgets and
      // waiting/pumping in our widget tests.
      Localization.load(
        const Locale('en'),
        translations: Translations(data),
      );
    
    
      await testMain();
    }
    

    【讨论】:

    • 你能举例说明你将如何使用它吗?
    【解决方案3】:

    这是因为 json 语言文件的加载方式。
    看看这个:

    // from flutter/lib/src/services/asset_bundle.dart
    
    Future<String> loadString(String key, { bool cache = true }) async {
        final ByteData data = await load(key);
        // Note: data has a non-nullable type, but might be null when running with
        // weak checking, so we need to null check it anyway (and ignore the warning
        // that the null-handling logic is dead code).
        if (data == null)
          throw FlutterError('Unable to load asset: $key'); // ignore: dead_code
        // 50 KB of data should take 2-3 ms to parse on a Moto G4, and about 400 μs
        // on a Pixel 4.
        if (data.lengthInBytes < 50 * 1024) {
          return utf8.decode(data.buffer.asUint8List());
        }
        // For strings larger than 50 KB, run the computation in an isolate to
        // avoid causing main thread jank.
        return compute(_utf8decode, data, debugLabel: 'UTF8 decode for "$key"');
    }
    

    如果文件大于一定大小,Flutter 使用compute 加载它并避免 UI 卡顿。

    如果在您的测试中,您在await tester.pumpAndSettle() 之前添加这一行,这将暂停主线程,给隔离完成并恢复执行一些时间。

    await Future.delayed(const Duration(milliseconds: 150), () {}); // this line
    await tester.pumpAndSettle()
    

    在我的笔记本电脑上,150 毫秒是持续通过测试的最短持续时间。

    【讨论】:

      猜你喜欢
      • 2020-09-18
      • 1970-01-01
      • 2021-01-08
      • 2019-07-11
      • 1970-01-01
      • 2021-01-13
      • 1970-01-01
      • 2018-08-16
      • 2019-03-23
      相关资源
      最近更新 更多