Flutter自动化测试辅助插件widget_driver_generator的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter自动化测试辅助插件widget_driver_generator的使用


pub package check-code-quality License

widget_driver_generator 是一个辅助包,它支持 widget_driver 包并生成所需的引导代码以完全设置您的 Drivers


使用

依赖

要使用 widget_driver_generator,你需要在 pubspec.yaml 中添加以下依赖:

dependencies:
  # ... 其他依赖项
  widget_driver: <最新版本>

dev_dependencies:
  # ... 其他开发依赖项
  build_runner: <最新版本>
  widget_driver_generator: <最新版本>

源代码

下面的指令解释了如何创建和注解您的 Drivers 以使用此生成器。除了导入 widget_driver 包之外,还需要包含一个引用生成的 Dart 文件的 part 指令。生成的文件名称始终为 [source_file].g.dart

import 'package:widget_driver/widget_driver.dart';

part 'this_file.g.dart';

在组织好导入语句之后,定义您的 Driver 并用 @GenerateTestDriver() 注解。如果您忘记这样做,则不会生成任何代码。

@GenerateTestDriver()
class MyDriver extends WidgetDriver {
  ...
}

现在只需一步即可完成。您的 Driver 中的任何公共属性和方法都可以被注解,以便它们在测试中提供特定值。所有简单的返回类型(如 Dart 的内置类型、枚举、可选类型和小部件中经常使用的类型,如 ColorIconDataFontWeight)都已涵盖。更复杂的类型(如自定义类)必须在您的代码中处理。

注解您想要在 TestDriver 中显式定义的属性,使用 @TestDriverDefaultValue({default_value})。作为 TestDriverDefaultValue 的参数,传递您希望驱动程序在测试时给小部件的默认值。

也可以用 @TestDriverDefaultValue({default_return_value}) 注解您的方法。作为 TestDriverDefaultValue 的参数,传递您希望驱动程序在测试期间小部件调用该方法时返回的默认值。如果方法不返回任何内容,则只需传递空参数。 如果方法返回一个未来,可以使用 @TestDriverDefaultFutureValue 注解。它将正确生成带有您传递的返回值的未来。

@GenerateTestDriver()
class MyDriver extends WidgetDriver {
  ...

  int get value => _someService.value;

  @TestDriverDefaultValue(CustomClass())
  CustomClass get value => _counterService.getCustomClass;

  void doSomething() {
    ...
  }

  String giveMeSomeString() {
    return _someService.getSomeString();
  }

  Future<int> giveMeSomeIntSoon() {
    return _someService.getSomeIntSoon();
  }

  @TestDriverDefaultFutureValue(CustomClass())
  Future<CustomClass> giveMeSomeCustomClassSoon() {
    return _someService.getSomeCustomClassSoon();
  }
}

(可选) 指定全局测试默认值

作为替代方案,您还可以在 build.yaml 中全局指定应该为类型生成什么值。这将加载您指定的值到从其中获取默认值的表中。

重要的是,您必须指定如何构造它们,而不是像类的 JSON 版本。注意这里,如何指定 CustomClass。确保用于生成类的驱动程序知道构造函数中使用的所有类,否则您会遇到编译错误。

pubspec.yaml 同级目录下添加 build.yaml,按以下模式:

targets:
  $default:
    builders:
      widget_driver_generator:
        options:
          defaultTestValues:
            "bool": "true"
            "Color": "Colors.yellow"
            "int": "123"
            "CustomClass": "const CustomClass(\n    name: 'name',\n    description: 'Some desc',\n    imageUrl: 'http://www.exampleImage.com/image',\n  )"
            "String": "'Hello World'"

这可以在多种情况下帮助您。上述示例做了两件事:

  1. 它覆盖了已经指定的内置和常用类型的值(布尔值、颜色、整数、字符串)。虽然不需要添加注释就可以使生成工作,但这更像是一个“锦上添花”。

  2. 它指定了 CustomClass 的值。如果没有注释,这个值将不会生成。通过在这里指定它,您可以省去在所有驱动程序中为该类添加注释的步骤。

生成代码

现在您已经完成了所有导入、定义和注解。
然后运行一次构建:

dart run build_runner build --delete-conflicting-outputs

更多关于使用 build_runner 的信息,请参阅 pub.dev


WidgetDriver简介

有关 widget_driver 框架的更多信息,请阅读 这里


示例代码

在项目中实现一个Driver

@GenerateTestDriver()
class MyDriver extends WidgetDriver {
  ...

  @TestDriverDefaultValue(1)
  int get value => _someService.value;

  @TestDriverDefaultValue()
  void doSomething() {
    ...
  }

  @TestDriverDefaultValue('The string')
  String giveMeSomeString() {
    return _someService.getSomeString();
  }

  @TestDriverDefaultFutureValue(123)
  Future<int> giveMeSomeIntSoon() {
    return _someService.getSomeIntSoon();
  }
}

你也可以将数据从小部件传递到驱动程序

class MyDriver extends WidgetDriver {
  final SomeType someNamedVariable;
  final SomeType somePositionalVariable;

  MyDriver(
    @driverProvidableProperty this.somePositionalVariable, {
    @driverProvidableProperty required this.someNamedVariable
  });

  ...
}

class MyWidget extends DriveableWidget<MyDriver> {

  @override
  Widget build(BuildContext context) {
    ...
  }

  @override
  WidgetDriverProvider<MyDriver> get driverProvider => $MyDriverProvider(
    somePositionalVariable: xyz,
    someNamedVariable: zyx,
  );
}

这适用于命名参数、位置参数和/或可选参数。

注意:请谨慎使用。这仅用于向驱动程序传递模型数据(例如点击列表项)。尽量不要用于提供存储库,以避免耦合并简化测试。

生成代码

为了生成 TestDriversWidgetDriverProviders,只需运行以下命令:

dart run build_runner build --delete-conflicting-outputs

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用widget_driver_generator插件来进行自动化测试辅助的示例。widget_driver_generator插件可以帮助你生成测试驱动代码,从而简化UI测试的编写过程。

步骤一:添加依赖

首先,你需要在你的pubspec.yaml文件中添加widget_driver_generator的依赖。

dev_dependencies:
  flutter_test:
    sdk: flutter
  widget_driver_generator: ^x.y.z  # 请替换为最新版本号

步骤二:运行生成器

在终端中运行以下命令来生成测试驱动代码:

flutter pub get
flutter pub run widget_driver_generator:generate

步骤三:编写Widget和测试

假设你有一个简单的Widget,比如CounterWidget,代码如下:

// counter_widget.dart
import 'package:flutter/material.dart';

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_count',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

步骤四:生成测试驱动代码

widget_driver_generator运行后,它会在你的测试目录中生成一个与CounterWidget对应的驱动文件,比如counter_widget_driver.dart

// counter_widget_driver.dart (自动生成)
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
  group('CounterWidget tests', () {
    FlutterDriver driver;

    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    tearDownAll(() async {
      if (driver != null) {
        driver.quit();
      }
    });

    test('tap on floating action button increments counter', async () async {
      // Find the counter text element
      SerializableFinder counterTextFinder = find.byValueKey('counterText');
      SerializableFinder fabFinder = find.byTooltip('Increment');

      // Wait for the counter text to appear
      String counterText = await driver.getText(counterTextFinder);
      expect(int.parse(counterText), 0);

      // Tap the floating action button
      await driver.tap(fabFinder);

      // Wait for the counter text to update
      await Future.delayed(Duration(seconds: 1));
      counterText = await driver.getText(counterTextFinder);
      expect(int.parse(counterText), 1);
    });
  });
}

注意:在实际生成的代码中,SerializableFinder可能需要根据你的Widget中的具体实现进行调整。例如,byValueKeybyTooltip可能需要替换为byText或其他适当的Finder。

步骤五:运行测试

你可以使用以下命令来运行Flutter的集成测试:

flutter drive --target=test_driver/app.dart

其中,app.dart是你的测试启动文件,通常内容如下:

// test_driver/app.dart
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:your_app/main.dart' as app; // 替换为你的主应用入口

void main() {
  enableFlutterDriverExtensions();
  app.main();
}

这样,你就可以使用widget_driver_generator生成的测试驱动代码来运行和验证你的Flutter应用的UI行为了。

回到顶部