“软件开发的核心原则之一就是能够对代码进行有效的测试”。
打开浏览器并输入URL
来验证刚写入的代码是否成功,这可不是非常可靠的测试方法;而且,在测试非GET
的HTTP
方法的端点时,我们也会遇到麻烦。 因此,编写测试需要采用一些有用的工具。
Dart 测试
Dart测试很简单:创建一个以_test.dart
结尾的文件,编写main
函数并使用test
函数注册一个测试。 每个测试都是一个可运行的并具有期望的代码闭包。 例如,这段代码会测试1 + 1 = 2
:
import 'package:test/test.dart';
void main() {
test("1+1 = 2", () {
expect(1 + 1, equals(2));
});
}
测试可以通过声明为依赖的test
包来实现。 在您的项目中找到文件pubspec.yaml
。 在该文件的底部,添加以下两行指定该项目使用test
包作为开发依赖项:
dev_dependencies:
test: any
举例说明
在Dart中,测试代码在项目的顶级test
目录中。我们首先创建一个名为test/books_controller_test.dart
的新文件。 (测试必须以_test.dart
结尾,并且位于test
目录中。)在这个文件中,导入应用程序的测试工具
import 'harness/app.dart';
测试工具导出test
包并声明一个名为TestApplication
的类。 在本地运行应用程序,执行请求并验证其响应。 TestApplication
可以从你的测试中运行应用程序,并且有一个client属性来执行针对该应用程序的请求。 在test/books_controller_test.dart
中,添加以下代码来设置测试工具:
Future main() async {
TestApplication app = new TestApplication();
setUpAll(() async {
await app.start();
});
tearDownAll(() async {
await app.stop();
});
}
很重要的一点,应用程序停止在
tearDownAll
中,否则你的测试不会退出,因为有一个HTTP服务器正在运行!
让我们添加一个测试来验证GET /books
返回图书列表。
void main() {
...
test("/books returns list of books", () async {
var request = app.client.request("/books");
expectResponse(
await request.get(),
200,
body: everyElement(predicate((x) => ((x['year'] <= 2018 && x['year'] >= 1900 )), "出版日期不对!"))
);
});
}
这个测试执行请求GET http://localhost/books
,并确保响应的状态码是200,而body
返回的图书信息列表中是正确的出版日期(例如不早于1900年,但不能迟于今年)。
expectResponse方法需要一个 TestResponse,状态码和一个可选的正文匹配器(它也可以选择一个header
匹配器)。 它会验证响应是否具有预期值。
TestResponse是通过执行TestRequest创建。TestRequest由TestApplication
的 client 创建的 request方法所创建, TestRequest 的执行方法包括 get(), post(), 等等.
Dart 的测试非常方便,右键单击它并从弹出菜单中选择“Run”运行此测试。 测试运行界面将出现在屏幕底部,并显示测试结果。
这里有一个小问题:everyElement
匹配器确保响应body
中的每个字符串都通过内部匹配器hasLength
。 然而,如果响应body
是一个空列表,内部匹配器将永远不会运行,并且测试仍然会通过。 让我们验证至少有一个本图书:
test("/books returns list of books", () async {
var request = app.client.request("/books");
expectResponse(
await request.get(),
200,
body: allOf([
hasLength(greaterThan(0)),
body: everyElement(predicate((x) => ((x['year'] <= 2018 && x['year'] >= 1900 )), "出版日期不对!"))
]));
});
编写更多测试
让我们再写两个测试 - 首先,确保GET /books/:index
返回一本图书,然后另一个确保图书范围之外的索引将返回404 。 在主函数中添加以下两个测试:
test("/books/index returns a single book", () async {
expectResponse(
await app.client.request("/books/1").get(),
200,
body: everyElement(predicate((x) => ((x['year'] <= 2018 && x['year'] >= 1900 )), "出版日期不对!"))
});
test("/books/index out of range returns 404", () async {
expectResponse(
await app.client.request("/books/100").get(),
404);
});
您可以通过右键单击main
函数并从弹出菜单中选择Run
来运行测试文件中的所有测试。
所有的测试都应该通过 。 然后,我们回到books_controller.dart
并故意添加了一个出版日期错误的新图书:
List books = [
new Book(
title: '将摄影还给大众——7天摄影入门',
author: '赵琳',
publisher: '清华大学出版社',
year: 2013),
new Book(
title: '物联网设计与应用(基于IPv6)',
author: '田景文 高美娟',
publisher: '清华大学出版社',
year: 2013),
new Book(
title: 'Rhino5.0产品造型设计基础教程',
author: '张铁成、孔祥富',
publisher: '清华大学出版社',
year: 2013),
## 新添加的错误数据
new Book(
title: '用Dart 搭建图书REST Framework',
author: '林慕空',
publisher: 'test publisher',
year: 2020
),
];
获取所有书籍的第一个测试将失败,并且以下内容将打印到控制台:
Expected: --- HTTP Response ---
- Status code must be 200
- Headers can be anything
- Body after decoding must be:
every element(出版日期不对!)
---------------------
Actual: TestResponse:<-----------
- Status code is 200
- Headers are the following:
- content-encoding: gzip
- content-length: 323
- x-frame-options: SAMEORIGIN
- content-type: application/json; charset=utf-8
- x-xss-protection: 1; mode=block
- x-content-type-options: nosniff
- server: aqueduct/1
[{title: 将摄影还给大众——7天摄影入门, author: 赵琳, publisher: 清华大学出版社, year: 2013}, {title: 物联网设计与应用(基于IPv6), author: 田景文 高美娟, publisher: 清华大学出版社, year: 2013}, {title: Rhino5.0产品造型设计基础教程, author: 张铁成、孔祥富, publisher: 清华大学出版社, year: 2013}, {title: 用Dart 搭建图书REST Framework, author: 林慕空, publisher: test publisher, year: 2020}]
-------------------------
>
Which: the body differs for the following reasons:
has value {
'title': '用Dart 搭建图书REST Framework',
'author': '林慕空',
'publisher': 'test publisher',
'year': 2020
} which doesn't match 出版日期不对! at index 3
package:test expect
package:aqueduct/src/testing/matchers.dart 232:3 expectResponse
test/books_controller_test.dart 24:7 main..
===== asynchronous gap ===========================
dart:async _Completer.completeError
test/books_controller_test.dart main..
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
test/books_controller_test.dart 21:51 main..
Process finished with exit code 1
原因是 《用Dart 搭建图书REST Framework》这本书的出版日期写错为2020年。
ps: 有个问题想请教一下,markdown 能用img
标签吗? 大家怎么添加emoji
表情符号呢?