Flutter自动化测试插件widget_driver_test的使用

Flutter自动化测试插件widget_driver_test的使用


pub package check-code-quality License

widget_driver_test 是一个使测试 WidgetDriversDrivableWidgets 更简单的包。它与 mocktail 一起使用以方便模拟。

要了解更多关于 WidgetDrivers 的信息,请阅读 widget_driver 的文档。

有关如何使用此包来测试您的小部件和驱动程序的灵感,请参阅示例应用程序中的测试 这里


测试 DrivableWidgets

创建一个模拟驱动器

当你在测试你的可驱动小部件时,你可能希望模拟驱动这些小部件的驱动器。

为了简化驱动器的模拟,我们创建了一个基类,你可以继承它。这为你提供了来自 mocktail 的功能,你可以在模拟实例上使用。

import 'package:mocktail/mocktail.dart';

...

class MockMyWidgetDriver extends MockDriver implements MyWidgetDriver {}

...

mockMyWidgetDriver = MockMyWidgetDriver();
when(() => mockMyWidgetDriver.title).thenReturn('Hey this is a mocked title');

在 DrivableWidget 中使用模拟驱动器

当你已经创建了你的模拟驱动器,你也希望在你的小部件中使用它。

为了让提供模拟驱动器到可驱动小部件变得容易,我们创建了一个辅助类。

创建一个 MockDriverProvider 实例,并将模拟驱动器作为值传递,将可驱动小部件作为子元素。

final myWidget = MockDriverProvider<MyWidgetDriver>(
    value: mockMyWidgetDriver,
    child: MyWidget(),
);

await tester.pumpWidget(myWidget);
// 现在进行你的小部件测试

在你的 DrivableWidgets 中使用真实的驱动器

如果你有一些特殊情况,你不希望使用 TestDrivers,那么你可以使用 WidgetDriverTestConfigProvider。它让你控制每个 DrivableWidget 是否创建 TestDriver 或真实驱动器。只需将要测试的小部件包裹在一个 WidgetDriverTestConfigProvider 中。

注意: 在大多数情况下,当你测试一个 DrivableWidget 时,你实际上想要抽象出任何子 DrivableWidget 中的真实实现逻辑,并只关注当前 DrivableWidget 及其直接逻辑。

如果你想在所有测试中都使用真实驱动器,可以这样做:

final myWidget = WidgetDriverTestConfigProvider(
  config: AlwaysUseRealDriversTestConfig(),
  child: MyWidget(),
);
await tester.pumpWidget(myWidget);

如果你只想在某些 DrivableWidgets 中使用真实驱动器,可以这样做:

final myWidget = WidgetDriverTestConfigProvider(
  config: UseRealDriversForSomeTestConfig(
    useRealDriversFor: { MyWidgetDriver }
  ),
  child: MyWidget(),
);
await tester.pumpWidget(myWidget);

测试 WidgetDrivers

要测试你的 Drivers,你需要使用 testWidgets 测试函数(就像你在测试小部件时一样)。

void main() {
    testWidgets('Some driver test', (WidgetTester tester) async {
        // 将你的驱动器测试代码放在这里
    }
}

创建一个驱动器

要创建你的驱动器,你需要一个辅助函数。 这是因为 Driver 需要通过 widget_driver 框架正确地构建。如果你自己创建驱动器的实例,则一些初始化阶段和驱动器的生命周期管理将无法工作。

为了解决这个问题,我们在 WidgetTester 上创建了一个辅助函数。

这是创建 Driver 的方法:

testWidgets('Some driver test', (WidgetTester tester) async {
    final driverTester = await tester.getDriverTester<MyWidgetDriver>(
          driverBuilder: () => MyWidgetDriver(theService: mockTheService),
          parentWidgetBuilder: (driverWidget) {
            return MultiProvider(
              providers: [
                Provider<SomeService>.value(value: mockSomeService),
                Provider<AnotherService>.value(value: mockAnotherService),
              ],
              child: driverWidget,
            );
          });
}

如你所见,你通过调用 tester.getDriverTester(...) 来创建 Driver。 这将返回一个 DriverTester。你使用这个 driverTester 来测试你的驱动器。

在这个例子中,我们创建了一个名为 MyWidgetDriver 的驱动器。它有从 BuildContext 解析的内部依赖项(SomeServiceAnotherService)以及一个通过构造函数传递的依赖项。

driverBuilder 中,你传递一个构建器来创建你的 Driver。在那里你可以提供通过构造函数传递的所有模拟依赖项。例如,theService

另外两个依赖项需要在驱动器创建时存在于 BuildContext 中。因此我们需要将这些依赖项的模拟版本放在驱动器之上的某个小部件中。这是通过 getDriverTester 方法的可选 parentWidgetBuilder 参数完成的。

在那里你可以传递一个小部件,该小部件将 driverWidget 作为子元素。这个 driverWidget 是包含 Driver 的小部件。

在我们的例子中,我们通过使用 Provider 包来传递模拟服务。

测试一个驱动器

一旦你有了 DriverTester,你就可以使用它来测试 Driver

driverTester 有一个名为 driver 的属性。这让你访问一个 Driver 的实例。这就是你在测试期间使用的 driver

testWidgets('Some driver test', (WidgetTester tester) async {
    final driverTester = await tester.getDriverTester<MyWidgetDriver>(...)

    final driver = driverTester.driver;
    expect(driver.buttonText, equals('The expected text'));

如果你的驱动器在其依赖项更改状态时更新小部件,那么你也可以轻松测试这一点,通过等待对 notifyWidget() 的调用。

testWidgets('当 isLoggedInStream 发射时 notifyWidgets 被调用', (WidgetTester tester) async {
    final driverTester = await tester.getDriverTester<LogInOutButtonDriver>(...)

    isLoggedInStreamController.add(true);
    isLoggedInStreamController.add(false);

    // 等待驱动器接收两次 notifyWidget 调用。
    await driverTester.waitForNotifyWidget(numberOfCalls: 2, requireExactNumberOfCalls: true);
    // 验证没有更多的 notifyWidget 调用
    await driverTester.verifyNoMoreCallsToNotifyWidget();

在这里我们有一个具有某些认证服务内部依赖的驱动器。每当认证服务更改登录状态时,驱动器将调用 notifyWidgets() 并更新小部件。

我们想验证驱动器是否真的调用了 notifyWidgets(),为此我们可以使用 driverTester 上的帮助函数。

有两个函数可以帮助我们。 首先,waitForNotifyWidget 将等待指定数量的 notifyWidgets() 调用。如果你从未收到足够的调用,waitForNotifyWidget 将超时并且测试将失败。你可以将超时持续时间作为参数传递给该方法,默认值为 1 秒。

其次,你可以使用 verifyNoMoreCallsToNotifyWidget 等待并检查是否没有更多的 notifyWidgets() 调用。你可以通过将超时持续时间作为参数传递给该方法来控制该方法等待和检查调用的时间。默认值为 1 秒。


示例代码

void main() {
  group('MyAppDriver:', () {
    late MockLocalization mockLocalization;

    setUp(() {
      mockLocalization = MockLocalization();
    });

    testWidgets('显示正确的应用标题', (WidgetTester tester) async {
      when(() => mockLocalization.appTitle).thenReturn('Some app title');

      final driverTester = await tester.getDriverTester<MyAppDriver>(
          driverBuilder: (context) => MyAppDriver(context),
          parentWidgetBuilder: (driverWidget) {
            return Provider<Localization>.value(
              value: mockLocalization,
              child: driverWidget,
            );
          });

      final driver = driverTester.driver;
      expect(driver.appTitle, equals('Some app title'));
    });
  });
}

更多关于Flutter自动化测试插件widget_driver_test的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自动化测试插件widget_driver_test的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用widget_driver_test插件进行自动化测试的示例。widget_driver_test通常与flutter_driver包一起使用,用于编写和执行端到端(E2E)测试。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加flutter_driver依赖:

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_driver:
    sdk: flutter

2. 创建测试文件

接下来,在你的项目根目录下创建一个新的目录,例如test_driver,并在其中创建一个新的Dart文件,例如app_test.dart。这个文件将包含你的自动化测试代码。

test_driver/app_test.dart

import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
  group('My Flutter App Tests', () {
    FlutterDriver driver;

    // Connect to the Flutter application before running any tests.
    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    // Close the connection to the driver after the tests have completed.
    tearDownAll(() async {
      if (driver != null) {
        driver.close();
      }
    });

    test('finds text in the app bar', () async {
      // Find the app bar by its tooltip and ensure it has the correct text.
      SerializableFinder appBarFinder = find.byTooltip('Open navigation menu');
      expect(await driver.getText(appBarFinder), 'My App');
    });

    test('taps on a button and checks the result', () async {
      // Find the button by its label text.
      SerializableFinder buttonFinder = find.text('Click Me');

      // Tap on the button.
      await driver.tap(buttonFinder);

      // Wait for a short duration to allow the UI to update.
      await Future.delayed(Duration(seconds: 1));

      // Verify the result, e.g., checking if a snackbar appears with specific text.
      SerializableFinder snackbarFinder = find.text('Button Clicked!');
      expect(await driver.waitFor(snackbarFinder), isTrue);
    });
  });
}

3. 运行测试

为了运行这些测试,你需要确保Flutter应用已经在模拟器或真实设备上运行。然后,在终端中导航到你的Flutter项目根目录,并运行以下命令:

flutter drive --target=test_driver/app_test.dart

这条命令会启动Flutter驱动程序,连接到正在运行的应用,并执行app_test.dart文件中定义的测试。

4. 注意事项

  • 确保你的Flutter应用入口文件(通常是lib/main.dart)中有如下代码,以启用驱动扩展:
void main() {
  enableFlutterDriverExtension();
  runApp(MyApp());
}
  • flutter_driver测试通常用于端到端测试,这意味着它们模拟用户交互并验证UI状态。对于更细粒度的单元测试,你可能想使用flutter_test包。

  • 使用SerializableFinder来定位UI元素,如find.byTooltipfind.byValueKeyfind.text等。

这个示例展示了如何使用widget_driver_test(实际上是通过flutter_driver包)进行基本的自动化测试。根据你的具体需求,你可以扩展这些测试来覆盖更多的应用功能和场景。

回到顶部