引言
平时开发中,app的各项功能测试工作一直都是由测试组来完成的,他们做的一般是黑盒测试(也称功能测试),其实我们开发人员已知产品的内部工作过程,可以通过编写自动化测试代码的方式测试证明每种内部操作是否符合设计规格要求,也就是白盒测试,所以接下来我就给大家介绍几种基本的测试方法。
白盒测试和黑盒测试
flutter中几种自动化测试的类型
1.单元测试(unit test)
2.组件测试(widget test)
3.集成测试(integration test)
这几种测试类型并不是独立存在的,一般而言,一个经过良好测试的app具有许多单元和组件测试,可按代码覆盖率进行跟踪,并具有足够的集成测试来涵盖所有重要的用例。
单元测试
单元测试顾名思义,是指测试单个函数、方法或者类。
单元测试的目的是在各种条件下验证逻辑单元的正确性。测试所依赖的一些外部数据或者操作一般通过模拟产生,并不会直接读取或操作服务器存储的数据。
那如何构建一个单元测试呢?
主要分为以下几步
1.添加测试包依赖
dev_dependencies:
flutter_test:
sdk: flutter
test: 1.15.2
2.创建需要被测试的函数、方法或者类和用来测试他的测试类
被测试的文件一般放在项目的lib目录下,而用来测试文件一般放在和lib同级的test目录下,一般以被测试文件名加_test后缀命名
现在我们在lib目录下创建一个名为Counter的类,它含有一个value属性和两个方法increment()和decrement()
/// a value counter
class Counter {
int value = 0;
void increment() => value++;
void decrement() => value--;
}
然后我们在test目录下创建counter_test文件,来测试Counter的increment()方法是否符合设计的预期
import 'package:test/test.dart';
import 'package:test_flutter_app/counter.dart';
void main() {
test('Counter value should incremented', () {
final counter = Counter();
/// invoke increment function
counter.increment();
/// verify result
expect(counter.value, 1);
});
}
3.运行测试
通过IDE(Android Studio或者VS Code)打开counter_test.dart,点击run menu就可运行
或者通过命令行方式运行
flutter test test/counter_test.dart
是不是很简单?实际上单元测试可以做的事情远比这多和复杂,限于篇幅,这里就不展开讲了
组件测试
flutter中我们最常使用的就是widget了,Flutter SDK中附带的flutter_test工具包中提供了几种工具,让我们来测试我们的widget
-
WidgetTester
在测试环境构建widget并和和其进行交互 -
testWidgets()
它会为每个测试用例自动创建一个新的WidgetTester,并代替常规的test()函数使用 -
Finder
用来在测试环境中寻找指定widget -
Matcher
验证指定widget是否符合预期
有了这几个基础工具,我们就可以来构建我们的widget测试了
分为以下几步
1.添加测试包依赖(和单元测试一致,省略)
2.创建一个待测试的widget
class MyWidget extends StatelessWidget {
final String title;
final String message;
const MyWidget({
Key key,
@required this.title,
@required this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
}
3.创建测试文件,编写测试逻辑
void main() {
// 定义一个widget测试,testWidgets()方法提供了一个WidgetTester用来在测试环境
// 构建需要测试的组件并与之交互
testWidgets('一个有自定义title和文本的widget', (WidgetTester tester) async {
// 通过tester构建待测试的widget
await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
// 创建finder,找到标题和文本
final titleFinder = find.text('T');
final messageFinder = find.text('M');
// 使用 flutter_test 提供的 `findsOneWidget` matcher 来匹配一个组件的情况
// 验证在tree上对应widget是否符合预期
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
基本测试流程为
1.调用testWidgets()方法,这个是测试入口
2.使用生成的WidgetTester构建我们要测试的Widget
3.使用find找到我们想验证的元素
4.调用expect方法并配合Matcher验证结果是否符合预期
4.运行测试(和单元测试类似,省略)
集成测试
单元测试和组件测试对于测试单个类,函数或widget都非常方便。但是,他们通常不会测试各个部分在整体上如何协同工作,也不会捕获在真实设备上运行的应用程序的性能。这些任务要通过集成测试来执行。
接下来我们就来一步一步实现一个最简单的集成测试案例
1.首先,创建一个待测试的app
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '计数器App',
home: MyHomePage(title: '计数器App首页'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你点击button的次数:',
),
Text(
'$_counter',
// 给点击次数文本设置一个key值,方便读取和验证结果
key: Key('counter'),
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
// 设置一个key给这个按钮,方便测试时寻找并点击它
key: Key('increment'),
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
功能非常简单,每点击一下button,计数并改变显示的文本数量
2.添加flutter_driver依赖
dev_dependencies:
flutter_test:
sdk: flutter
test: 1.15.2
flutter_driver:
sdk: flutter
3.创建测试文件
不同于单元测试和组件测试,集成测试需要创建两个文件
1.一个文件用来创建可测试的Flutter应用程序
2.一个文件用来运行测试指令集合
counter_app/
lib/
main.dart
test_driver/
app.dart
app_test.dart
本例中,在test_driver目录下创建了app.dart和app_test.dart,注意测试文件必须和创建可测试的Flutter应用程序文件名一致,并以 _test 结尾
4.创建可测试的Flutter应用程序
void main() {
// 开启flutter测试驱动扩展
enableFlutterDriverExtension();
// 启动你想测试的app或者应用
app.main();
}
5.编写测试用例
void main() {
group('计数器App', () {
// 首先通过findre找到我们这次测试中需要使用到的两个widget
final counterTextFinder = find.byValueKey('counter');
final buttonFinder = find.byValueKey('increment');
FlutterDriver driver;
// 连接 flutter driver
setUpAll(() async {
driver = await FlutterDriver.connect();
});
// 测试用例跑完后断开连接
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('初始计数值 0', () async {
// 验证计数器初始值
expect(await driver.getText(counterTextFinder), "0");
});
test('点击按钮增加计数', () async {
// 模拟点击增加计数按钮
await driver.tap(buttonFinder);
// 验证计数器值变为1
expect(await driver.getText(counterTextFinder), "1");
});
});
}
6.运行测试
在命令行输入以下指令
flutter driver --target=你的工程路径/test_driver/app.dart
运行结果
lihuifeng@lihuifengdeMacBook-Pro test_flutter_app % flutter driver --target=/Volumes/WorkSpace/test_flutter_app/test_driver/app.dart
Using device vivo NEX A.
Starting application: /Volumes/WorkSpace/test_flutter_app/test_driver/app.dart
Installing build/app/outputs/flutter-apk/app.apk... 7.0s
Running Gradle task 'assembleDebug'...
Running Gradle task 'assembleDebug'... Done 3.6s
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
I/flutter (16989): Observatory listening on http://127.0.0.1:40395/Dmwm7lXlm2o=/
00:00 +0: 计数器App (setUpAll)
VMServiceFlutterDriver: Connecting to Flutter application at http://127.0.0.1:63926/Dmwm7lXlm2o=/
VMServiceFlutterDriver: Isolate found with number: 2159525238303759
VMServiceFlutterDriver: Isolate is paused at start.
VMServiceFlutterDriver: Attempting to resume isolate
VMServiceFlutterDriver: Connected to Flutter application.
00:02 +0: 计数器App 初始计数值 0
00:02 +1: 计数器App 点击按钮增加计数
00:02 +2: 计数器App (tearDownAll)
00:02 +2: All tests passed!
Stopping application instance.
至此flutter测试的几种方法都介绍完了,当然这只是最基础的用法,后续大家有兴趣,我可以结合项目写一些能实际使用的例子再给大家分享