Flutter测试框架插件angel3_test的使用

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

Flutter测试框架插件angel3_test的使用

angel3_test 是一个用于 Angel3 框架的测试工具库。通过该库,你可以方便地编写和运行测试用例,确保你的应用在不同情况下都能正常工作。

TestClient

TestClient 类是一个自定义的 angel3_client,它可以向你的服务器发送模拟请求。这意味着你无需将服务器绑定到 HTTP 即可运行测试。此外,它支持服务和其他功能。TestClient 还支持 WebSocket。WebSocket 在此库中无法模拟(目前),因此调用 websocket() 函数会将你的服务器绑定到 HTTP(如果尚未监听)。返回值是一个 WebSockets 客户端实例(来自 package:angel3_websocket)。

var ws = await client.websocket('/ws');
ws.service('api/users').onCreated.listen(...);

// 接收 WebSocket 上发送的所有数据块:
ws.onData.listen(...);

Matchers

该包包含多个 Matcher,它们可以应用于任何 package:httpResponse,而不仅仅是那些由 Angel 返回的响应。

void test('foo', () async {
    var res = await client.get('/foo');
    expect(res, allOf([
        isJson({'foo': 'bar'}),  // 响应体是 JSON 并且内容为 {"foo": "bar"}
        hasStatus(200),          // 状态码为 200
        hasContentType(ContentType.JSON),  // 内容类型为 JSON
        hasContentType('application/json'),  // 内容类型为 application/json
        hasHeader('server'),     // 存在 server 头
        hasHeader('server', 'angel'),  // server 头的值为 angel
        hasHeader('foo', ['bar', 'baz']),  // foo 头的值为 bar 或 baz
        hasBody(),               // 响应体不为空
        hasBody('{"foo":"bar"}') // 响应体为 {"foo":"bar"}
    ]));
});

void test('error', () async {
    var res = await client.get('/error');
    expect(res, isAngelHttpException());  // 响应是 AngelHttpException
    expect(res, isAngelHttpException(statusCode: 404, message: ..., errors: [...]))  // 可选参数
});

hasValidBody 是此库中最强大的 Matcher 之一,因为它允许你根据验证模式验证 JSON 响应体。Angel3 提供了一个综合的验证库,与你已经使用的 matcher 包紧密集成。

test('validate response', () async {
    var res = await client.get('/bar');
    expect(res, hasValidBody(Validator({
        'foo': isBoolean,
        'bar': [isString, equals('baz')],
        'age*': [],
        'nested': someNestedValidator
    })));
});

完整示例

以下是一个完整的示例,展示了如何使用 angel3_test 来测试一个简单的 Angel3 应用程序。

import 'dart:io';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_test/angel3_test.dart';
import 'package:angel3_validate/angel3_validate.dart';
import 'package:angel3_websocket/server.dart';
import 'package:test/test.dart';

void main() {
  Angel app;
  late TestClient client;

  setUp(() async {
    app = Angel()
      ..get('/hello', (req, res) => 'Hello')
      ..get(
          '/error',
          (req, res) =>
              throw AngelHttpException.forbidden(message: 'Test')
                ..errors.addAll(['foo', 'bar']))
      ..get('/body', (req, res) {
        res
          ..write('OK')
          ..close();
      })
      ..get(
          '/valid',
          (req, res) =>
              {
                'michael': 'jackson',
                'billie': {'jean': 'hee-hee', 'is_my_lover': false}
              })
      ..post('/hello', (req, res) async {
        var body = await req.parseBody().then((_) => req.bodyAsMap);
        return {'bar': body['foo']};
      })
      ..get('/gzip', (req, res) async {
        res
          ..headers['content-encoding'] = 'gzip'
          ..add(gzip.encode('Poop'.codeUnits));
        await res.close();
      })
      ..use(
          '/foo',
          AnonymousService(
              index: ([params]) async => [
                    {'michael': 'jackson'}
                  ],
              create: (dynamic data, [params]) async => {'foo': 'bar'}));

    var ws = AngelWebSocket(app);
    await app.configure(ws.configureServer);
    app.all('/ws', ws.handleRequest);

    app.errorHandler = (e, req, res) => e.toJson();

    client = await connectTo(app);
  });

  tearDown(() async {
    await client.close();
  });

  group('matchers', () {
    group('isJson+hasStatus', () {
      test('get', () async {
        final response = await client.get(Uri.parse('/hello'));
        expect(response, isJson('Hello'));
      });

      test('post', () async {
        final response =
            await client.post(Uri.parse('/hello'), body: {'foo': 'baz'});
        expect(response, allOf(hasStatus(200), isJson({'bar': 'baz'})));
      });
    });

    test('isAngelHttpException', () async {
      var res = await client.get(Uri.parse('/error'));
      print(res.body);
      expect(res, isAngelHttpException());
      expect(
          res,
          isAngelHttpException(
              statusCode: 403, message: 'Test', errors: ['foo', 'bar']));
    });

    test('hasBody', () async {
      var res = await client.get(Uri.parse('/body'));
      expect(res, hasBody());
      expect(res, hasBody('OK'));
    });

    test('hasHeader', () async {
      var res = await client.get(Uri.parse('/hello'));
      expect(res, hasHeader('server'));
      expect(res, hasHeader('server', 'angel'));
      expect(res, hasHeader('server', ['angel']));
    });

    test('hasValidBody+hasContentType', () async {
      var res = await client.get(Uri.parse('/valid'));
      expect(res, hasContentType('application/json'));
      expect(
          res,
          hasValidBody(Validator({
            'michael*': [isString, isNotEmpty, equals('jackson')],
            'billie': Validator({
              'jean': [isString, isNotEmpty],
              'is_my_lover': [isBool, isFalse]
            })
          })));
    });

    test('gzip decode', () async {
      var res = await client.get(Uri.parse('/gzip'));
      expect(res, hasHeader('content-encoding', 'gzip'));
      expect(res, hasBody('Poop'));
    });

    group('service', () {
      test('index', () async {
        var foo = client.service('foo');
        var result = await foo.index();
        expect(result, [
          {'michael': 'jackson'}
        ]);
      });

      test('create', () async {
        var foo = client.service('foo');
        var result = await foo.create({});
        expect(result, {'foo': 'bar'});
      });
    });

    test('websocket', () async {
      var ws = await client.websocket();
      var foo = ws.service('foo');
      await foo.create({});
      var result = await foo.onCreated.first;
      expect(result.data, equals({'foo': 'bar'}));
    });
  });
}

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

1 回复

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


当然,angel3_test 是一个用于 Dart 和 Flutter 应用的测试框架插件,它基于 Angel 框架的测试工具。尽管 angel3_test 主要用于后端服务测试,但你也可以在 Flutter 应用中对相关的 Dart 代码进行单元测试。以下是一个简单的示例,展示如何在 Flutter 项目中使用 angel3_test 进行测试。

首先,确保你已经在 pubspec.yaml 文件中添加了 angel3_test 依赖:

dependencies:
  flutter:
    sdk: flutter
  # 其他依赖...

dev_dependencies:
  test: ^1.16.0
  angel3_test: ^x.y.z  # 请替换为最新版本号

然后,运行 flutter pub get 以获取依赖。

接下来,创建一个测试文件,例如 test/example_test.dart。在这个文件中,我们将编写一个简单的测试来演示如何使用 angel3_test

import 'package:test/test.dart';
import 'package:angel3_test/angel3_test.dart';
import 'package:angel3_framework/angel3_framework.dart';

void main() {
  group('Angel App Tests', () {
    late Angel app;

    setUp(() async {
      app = Angel();
      
      // 注册一些简单的路由用于测试
      app.get('/hello', (req, res) async {
        res.write('Hello, World!');
      });

      // 初始化应用(如果有必要)
      await app.configure(server);
    });

    tearDown(() async {
      // 清理资源
      await app.close();
    });

    test('GET /hello returns Hello, World!', () async {
      var response = await request(app).get('/hello');
      expect(response.statusCode, equals(200));
      expect(response.body, equals('Hello, World!'));
    });

    // 你可以添加更多的测试用例
    // test('POST /example does something', () async { ... });
  });
}

在这个示例中,我们做了以下几件事:

  1. 引入必要的包testangel3_test
  2. 创建测试组:使用 group 函数来组织相关的测试用例。
  3. 设置和清理:使用 setUptearDown 函数在每个测试用例之前和之后进行初始化和清理工作。
  4. 编写测试用例:使用 test 函数定义一个具体的测试用例,通过 request(app).get('/hello') 发送 GET 请求到 /hello 路由,并验证响应的状态码和响应体。

请注意,serverapp.configure(server); 中被引用,但在上述示例中并未定义。在完整的后端应用中,你可能会初始化一个 HTTP 服务器实例并将其传递给 app.configure。然而,在 Flutter 测试环境中,通常不需要这样做,因为 angel3_test 提供了模拟请求和响应的工具。如果你只是测试路由处理逻辑,可以忽略 app.configure(server); 这一行。

此外,由于 Flutter 主要用于前端开发,而后端服务测试通常不在 Flutter 测试套件中进行,因此在实际 Flutter 应用中,你可能会更倾向于使用 http 包或其他网络请求库来模拟后端请求,并使用 test 包进行单元测试。angel3_test 更适合在纯 Dart 后端服务或包含 Dart 后端服务的 Flutter 应用的后端部分进行测试。

回到顶部