使用 easy_localization 和大翻译 json 文件进行 Flutter 测试
Flutter Test with easy_localization and big translation json file
我在 flutter 项目中使用 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 个 print
s 应该分别打印 builder1
和 builder2
.
当我在测试中使用 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!
我该如何解决这个问题?
这是因为 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 毫秒是持续通过测试的最短持续时间。
最后,我通过添加文件 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();
}
当您使用 CodegenLoader 时,这是一个可用于设置简单本地化单例的小方法,类似于 Valentin Vignal 的 json 示例,我还建议使用不带回调参数的小函数而不是嵌套你的设置函数,至少如果你不对回调本身进行操作。这样很明显(仅!)正在发生副作用。
void addLocalization() {
Localization.load(
const Locale('en'),
translations: Translations(CodegenLoader.en),
);
}
我在 flutter 项目中使用 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 个 print
s 应该分别打印 builder1
和 builder2
.
当我在测试中使用 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!
我该如何解决这个问题?
这是因为 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 毫秒是持续通过测试的最短持续时间。
最后,我通过添加文件 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();
}
当您使用 CodegenLoader 时,这是一个可用于设置简单本地化单例的小方法,类似于 Valentin Vignal 的 json 示例,我还建议使用不带回调参数的小函数而不是嵌套你的设置函数,至少如果你不对回调本身进行操作。这样很明显(仅!)正在发生副作用。
void addLocalization() {
Localization.load(
const Locale('en'),
translations: Translations(CodegenLoader.en),
);
}