flutter_i18n
flutter_i18n copied to clipboard
Not sure how to widget test
I had originally used the basic Flutter internationalization strategy and even managed to widget test it using @remonh87 workaround (https://medium.com/flutterpub/testing-localized-widget-in-flutter-3bfa5492bc84)
But, like most people, this was just too much boilerplate and this plugin makes life much better. However, now my widget tests don't work and I am not sure how to implement a solution like @remonh87, since the underlying class is now autogenerated (which is why this is such a great tool to begin with!)
I was wondering if anyone else has been able to get widget testing using the localized strings working before I looked into doing a PR.
@2cData I think you can still use this plugin and have proper widget tests. If you do not care about testing the localized strings you can simply inject a stubtestdelegate in your widget test and test the screen. If you do care about strings according to the flutter team you can solve it by tester.runAsync
according to issue https://github.com/flutter/flutter/issues/22193 . Never test this myself though
https://github.com/flutter/flutter/issues/22193#issuecomment-507793123 @chunhtai actually it doesn't work.
In our case, we are using .json files to do translations. So we have like these:
- assets/lang/en.json
- assets/lang/pt.json
- assets/lang/es.json
.....
The way we load and use these json files are through LocalizationsDelegate extension like this:
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
static AppLocalizations of(BuildContext context) =>
Localizations.of<AppLocalizations>(context, AppLocalizations);
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
Map<String, String> _localizedStrings;
Future<bool> load() async {
try {
String jsonString = await rootBundle.loadString('assets/lang/${locale.languageCode}.json'); **here is the problem**
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) => MapEntry(key, value));
return true;
} catch (e) {
return false;
}
}
String translate(String key) => _localizedStrings[key];
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
return ['en', 'pt', 'es'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}
class MyTranslatedWidget extends StatefulWidget {
@override
_MyTranslatedWidgetState createState() => _MyTranslatedWidgetState();
}
class _MyTranslatedWidget extends State<MyTranslatedWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text(
AppLocalizations.of(context).translate('hello_key'),
),
);
}
}
and then in our widget test, we have something like this:
testWidgets(
'my test description',
(WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(MaterialApp(
locale: Locale('en'),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
AppLocalizations.delegate **here is the problem**
],
home: MyTranslatedWidget(),
));
final titleFinder = find.text('hello');
expect(titleFinder, findsOneWidget);
});
},
);
After running this test we got the following problem:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown while running async test code:
Expected: exactly one matching node in the widget tree
Actual: ?:<zero widgets with text "hello" (ignoring offstage widgets)>
Which: means none were found but one was expected
This happens because,AppLocalizations.delegate
that is called on localizationsDelegates
from MaterialApp
is not able to load the .json
file that have all the text translations.
Something happens here: String jsonString = await rootBundle.loadString('assets/lang/${locale.languageCode}.json');
it seems like the rootBundle can't find or load the json assets...
Yes, @ViniciusSossela https://github.com/ViniciusSossela, I saw your comments and your code. You actually saved me some time because my translations file was going to get too big to work at some point, so thanks for that. The testDelegate also did not work for me; it still threw an NPE on the getter for the translated strings.
I'm actually working on not using the plugin, going back to manually creating the arb and using the isTest option described in https://medium.com/flutterpub/testing-localized-widget-in-flutter-3bfa5492bc84, which is based on @Rémon 's comments. I would be satisfied confirming that the keys are actually being used on the page. Internationalization in Flutter without this plugin is pretty clumsy, but bypassing TDD just to sneak past CI isn't really justifiable.
On Tue, Nov 19, 2019 at 9:58 AM Vinicius Sossella [email protected] wrote:
flutter/flutter#22193 (comment) https://github.com/flutter/flutter/issues/22193#issuecomment-507793123 @chunhtai https://github.com/chunhtai actually it doesn't work.
In our case, we are using .json files to do translations. So we have like these:
- assets/lang/en.json
- assets/lang/pt.json
- assets/lang/es.json
.....
The way we load and use these json files are through LocalizationsDelegate extension like this:
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
static AppLocalizations of(BuildContext context) =>
Localizations.of<AppLocalizations>(context, AppLocalizations);
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
Map<String, String> _localizedStrings;
Future
load() async { try { String jsonString = await rootBundle.loadString('assets/lang/${locale.languageCode}.json'); **here is the problem** Map<String, dynamic> jsonMap = json.decode(jsonString); _localizedStrings = jsonMap.map((key, value) => MapEntry(key, value)); return true; } catch (e) { return false; }
}
String translate(String key) => _localizedStrings[key];
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
return ['en', 'pt', 'es'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = AppLocalizations(locale); await localizations.load(); return localizations;
}
@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}
and then in our widget test, we have something like this:
testWidgets(
'my test description', (WidgetTester tester) async { await tester.runAsync(() async { await tester.pumpWidget(MaterialApp( locale: Locale('en'), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, AppLocalizations.delegate **here is the problem** ], home: MyTranslatedWidget(), )); final titleFinder = find.text('hello'); expect(titleFinder, findsOneWidget); }); },
);
After running this test we got the following problem:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown while running async test code:
Expected: exactly one matching node in the widget tree
Actual: ?:<zero widgets with text "hello" (ignoring offstage widgets)>
Which: means none were found but one was expected
This happens because,AppLocalizations.delegate that is called on localizationsDelegates from MaterialApp is not able to load the .json file that have all the text translations.
Something happens here: String jsonString = await rootBundle.loadString('assets/lang/${locale.languageCode}.json');
it seems like the rootBundle can't find or load the json assets...
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/long1eu/flutter_i18n/issues/116?email_source=notifications&email_token=ABDXO4S4PTW6DXB7QFSJNGTQUP5H3A5CNFSM4JIJJ622YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEOPHBQ#issuecomment-555545478, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDXO4W5ALXLNEX7FLTRR7LQUP5H3ANCNFSM4JIJJ62Q .
-- Dave Callaghan Senior Developer
linkedin.com/in/mrdavidcallaghan facebook.com/mrdavidcallaghan skype: mr.david.callaghan