为什么一些 Flutter 组件测试同时执行时失败,但单独执行时则通过?

6

当在同一个文件中运行多个测试,并依次运行每个测试时,某些类型的测试会失败。但是,当单独运行这些测试时,它们通过了相同的测试。

目前这是我的测试文件,虽然有点长:

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

1
如果你的测试单独通过但是一起运行时失败了,那就说明你的测试不是隔离的:也就是说,在某个地方保留了跨测试的某种状态。提供一个最小化且可重现的示例会有所帮助。 - jamesdlin
哦,谢谢,有道理。虽然我唯一与状态相关的是flutter_localizations,但它可能会引起问题吗?我将尝试设置一个示例。 - Keff
发现问题了,与本地化有关。是否有办法在每个测试之前重置状态?或者你建议如何处理?再次感谢! - Keff
我又来了,哈哈。我在这个答案中找到了解决方法:https://dev59.com/_67la4cB1Zd3GeqPf5oI#52474073。 - Keff
2个回答

8

好的,我找到了解决方案和原因,感谢问题和SO问题的帮助。

主要问题在于使用从JSON文件加载的本地化(Localizations)。解决方法是将每个测试用例包装在tester.runAsync()中。

testWidgets('widget test 2', (WidgetTester tester) async {
  await tester.runAsync(() async {
    // tests
  });
});

1
谢谢,这对我很有帮助。我已经在这个问题上浪费了几个小时了。 - Smundo
但对我来说没有用... - Kira Resari

1
我想这个问题出现在不同的测试没有创建不同的小部件实例(或者它们以其他方式冲突),但是有一个解决方法可能会对您有所帮助。
如果您想创建一个运行所有测试的单个按钮 - 您可以创建一个批处理文件(在Windows上为cmd),该文件按顺序运行所有测试。
示例all_tests.cmd
dart test_1.dart
dart test_2.dart
dart test_3.dart

这不是最清晰的解决方案,但对于第一次可能有效。

是的,这在过去有些时候是我的解决方案,也许应该把它添加到问题中。我希望能够让我知道确切的问题,因为我想将所有相关的测试放在一个文件中。我已经看到Flutter在他们的示例中做到了这一点,所以这是可以做到的...无论如何,谢谢! - Keff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接