为什么一些 Flutter 小部件测试如果一起执行会失败但单独通过?
Why do some Flutter widget tests fail if executed together but pass individually?
在同一个文件中有多个测试,并且 运行一个接一个地进行测试。某些类型的测试失败,但相同的测试在 运行 单独时通过。
这是我目前的测试文件,虽然有点长:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rec/Components/Inputs/text_fields/DniTextField.dart';
import 'package:rec/Helpers/Validators.dart';
import '../../test_utils.dart';
void main() {
testWidgets('DniTextField works with invalid DNI', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(
TestUtils.wrapPublicRoute(widget),
);
await tester.pumpAndSettle();
// Test that widget has at least rendered
TestUtils.widgetExists(widget);
// Enter text into field with
var widgetFinder = find.byWidget(widget);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, 'invaliddni');
await tester.pumpAndSettle();
expect(onChangedResult, 'invaliddni');
expect(formKey.currentState.validate(), false);
});
testWidgets('DniTextField works with valid DNI', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(
TestUtils.wrapPublicRoute(widget),
);
await tester.pumpAndSettle();
// Enter text into field
var widgetFinder = find.byType(DniTextField);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, '80008000k');
await tester.pumpAndSettle();
expect(onChangedResult, '80008000k');
expect(formKey.currentState.validate(), true);
});
testWidgets('DniTextField works with valid DNI with trailing space', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(TestUtils.wrapPublicRoute(widget));
await tester.pumpAndSettle();
// Enter text into field
var widgetFinder = find.byWidget(widget);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, '80008000k ');
await tester.pumpAndSettle();
// The value emitted by the field, should be free of trailing whitespace
expect(onChangedResult, '80008000k');
expect(formKey.currentState.validate(), true);
});
}
如果我运行这样:
$ flutter test test/Components/inputs/dni_text_field_test.dart
第一个测试通过,但接下来的两个测试没有通过,出现以下错误:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
The finder "zero widgets with the given widget
(DniTextField-[LabeledGlobalKey<DniTextFieldState>#0fb86]) (ignoring offstage widgets)" (used in a
call to "tap()") could not find any matching widgets.
When the exception was thrown, this was the stack:
#0 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:902:7)
#1 WidgetController.getCenter (package:flutter_test/src/controller.dart:841:12)
#2 WidgetController.tap (package:flutter_test/src/controller.dart:273:18)
#3 main.<anonymous closure> (file:///[masked]/test/Components/inputs/dni_text_field_test.dart:99:18)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
如果我然后注释掉第一个测试,那么第二个通过但第三个没有通过。正如我所说,如果我 运行 他们单独进行,测试就会通过。
我找不到任何关于此的信息,连一件事都没有。也许这里有人可以指导我朝着正确的方向前进。我对 Flutter 小部件测试有点菜鸟,所以我可能会遗漏一些重要的东西。
我目前找到的唯一解决方案是将每个测试放在一个单独的文件中。虽然这并不理想,但在同一个文件中包含相关测试会更好。 Flutter 有示例显示每个文件允许多次测试,这是应该的。
Flutter 版本:2.4.0-5.0.pre.87
我想当不同的测试没有创建不同的小部件实例时(或者它们在其他方面有冲突),我想这个问题就会发生,但是有一个转变可能对你有帮助。
如果您想创建一个按钮 运行 所有测试 - 您可以创建一个 bash(或 windows 上的 cmd)文件,即 运行按顺序进行所有测试。
示例all_tests.cmd
dart test_1.dart
dart test_2.dart
dart test_3.dart
这不是最清晰的解决方案,但它可能是第一次使用。
好的,我找到了解决方案和原因,感谢 this issue and SO 问题。
主要问题是使用从 JSON 文件加载的本地化。解决方案是用 tester.runAsync()
包装每个测试
testWidgets('widget test 2', (WidgetTester tester) async {
await tester.runAsync(() async {
// tests
});
});
在同一个文件中有多个测试,并且 运行一个接一个地进行测试。某些类型的测试失败,但相同的测试在 运行 单独时通过。
这是我目前的测试文件,虽然有点长:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rec/Components/Inputs/text_fields/DniTextField.dart';
import 'package:rec/Helpers/Validators.dart';
import '../../test_utils.dart';
void main() {
testWidgets('DniTextField works with invalid DNI', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(
TestUtils.wrapPublicRoute(widget),
);
await tester.pumpAndSettle();
// Test that widget has at least rendered
TestUtils.widgetExists(widget);
// Enter text into field with
var widgetFinder = find.byWidget(widget);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, 'invaliddni');
await tester.pumpAndSettle();
expect(onChangedResult, 'invaliddni');
expect(formKey.currentState.validate(), false);
});
testWidgets('DniTextField works with valid DNI', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(
TestUtils.wrapPublicRoute(widget),
);
await tester.pumpAndSettle();
// Enter text into field
var widgetFinder = find.byType(DniTextField);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, '80008000k');
await tester.pumpAndSettle();
expect(onChangedResult, '80008000k');
expect(formKey.currentState.validate(), true);
});
testWidgets('DniTextField works with valid DNI with trailing space', (WidgetTester tester) async {
var key = GlobalKey<DniTextFieldState>();
var formKey = GlobalKey<FormState>();
var onChangedResult;
var widget = Form(
key: formKey,
child: DniTextField(
key: key,
onChange: (String value) {
onChangedResult = value;
},
validator: (s) => Validators.verifyIdentityDocument(s),
),
);
await tester.pumpWidget(TestUtils.wrapPublicRoute(widget));
await tester.pumpAndSettle();
// Enter text into field
var widgetFinder = find.byWidget(widget);
await tester.tap(widgetFinder);
await tester.showKeyboard(widgetFinder);
await tester.enterText(widgetFinder, '80008000k ');
await tester.pumpAndSettle();
// The value emitted by the field, should be free of trailing whitespace
expect(onChangedResult, '80008000k');
expect(formKey.currentState.validate(), true);
});
}
如果我运行这样:
$ flutter test test/Components/inputs/dni_text_field_test.dart
第一个测试通过,但接下来的两个测试没有通过,出现以下错误:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
The finder "zero widgets with the given widget
(DniTextField-[LabeledGlobalKey<DniTextFieldState>#0fb86]) (ignoring offstage widgets)" (used in a
call to "tap()") could not find any matching widgets.
When the exception was thrown, this was the stack:
#0 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:902:7)
#1 WidgetController.getCenter (package:flutter_test/src/controller.dart:841:12)
#2 WidgetController.tap (package:flutter_test/src/controller.dart:273:18)
#3 main.<anonymous closure> (file:///[masked]/test/Components/inputs/dni_text_field_test.dart:99:18)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
如果我然后注释掉第一个测试,那么第二个通过但第三个没有通过。正如我所说,如果我 运行 他们单独进行,测试就会通过。
我找不到任何关于此的信息,连一件事都没有。也许这里有人可以指导我朝着正确的方向前进。我对 Flutter 小部件测试有点菜鸟,所以我可能会遗漏一些重要的东西。
我目前找到的唯一解决方案是将每个测试放在一个单独的文件中。虽然这并不理想,但在同一个文件中包含相关测试会更好。 Flutter 有示例显示每个文件允许多次测试,这是应该的。
Flutter 版本:2.4.0-5.0.pre.87
我想当不同的测试没有创建不同的小部件实例时(或者它们在其他方面有冲突),我想这个问题就会发生,但是有一个转变可能对你有帮助。
如果您想创建一个按钮 运行 所有测试 - 您可以创建一个 bash(或 windows 上的 cmd)文件,即 运行按顺序进行所有测试。
示例all_tests.cmd
dart test_1.dart
dart test_2.dart
dart test_3.dart
这不是最清晰的解决方案,但它可能是第一次使用。
好的,我找到了解决方案和原因,感谢 this issue and
主要问题是使用从 JSON 文件加载的本地化。解决方案是用 tester.runAsync()
testWidgets('widget test 2', (WidgetTester tester) async {
await tester.runAsync(() async {
// tests
});
});