编写测试代码--用Dart 搭建图书REST Framework

“软件开发的核心原则之一就是能够对代码进行有效的测试”

打开浏览器并输入URL来验证刚写入的代码是否成功,这可不是非常可靠的测试方法;而且,在测试非GETHTTP方法的端点时,我们也会遇到麻烦。 因此,编写测试需要采用一些有用的工具。

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年。
laughing.png

ps: 有个问题想请教一下,markdown 能用img 标签吗? 大家怎么添加emoji表情符号呢?

你可能感兴趣的:(编写测试代码--用Dart 搭建图书REST Framework)