flutterlocalizationflutter-testflutter-localizations

Flutter Test with easy_localization and big translation json file


I'm using easy_localization in a flutter project. I need to write some widget tests.

But it looks like that when the .json file with the translations is too big, the MaterialApp never builds its child and therefore, I cannot test my widgets.

Here is the architecture of my small reproducible project:

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

Here is my test_test.dart file:

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 (a small file):

{
  "0": "0"
}

ru.json (a big file):

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

In my test, I have 2 prints which should respectively print builder1 and builder2.

This works well when I use Locale('sv') in my test:

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

But when I use Locale('ru'), MaterialApp doesn't build its child and I don't get the print builder2:

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

How can I fix this?


Solution

  • In the end, I fixed it by adding a file test/flutter_test_config.dart. I got the inspiration from 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();
    }