欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里插入图片描述

前言

如果你写过 Dart/Flutter 测试,你一定熟悉 flutter_test 中的 expect(actual, matcher) 语法。
这套语法虽然经典,但有几个缺点:

  1. 阅读不直观expect(user.age, greaterThan(18)) 读起来像 Yoda code。
  2. 自动补全弱:IDE 不知道 user.age 是 int,所以不会智能提示 greaterThan
  3. 错误信息有时含糊:只能报 “Expected: >18, Actual: 10”,难以展示复杂对象的差异。

checks 是 Dart 团队推出的下一代测试断言库。它采用了 Fluent API 风格,让测试代码读起来像英语句子,并且利用 Dart 的类型系统提供了强大的 IDE 自动补全。

对于 OpenHarmony 应用,编写高质量的测试是保证代码质量的关键。checks 能让你的测试代码更易写、更易读、更易维护。

一、核心对比:Expect vs Checks

传统方式 (expect):

test('user test', () {
  expect(user.name, equals('Alice'));
  expect(user.age, greaterThan(18));
  expect(user.tags, contains('admin'));
});

现代方式 (checks):

test('user test', () {
  check(user).name.equals('张三');
  check(user).age.isGreaterThan(18);
  check(user).tags.contains('admin');
});

注意到了吗?check(user) 后,IDE 知道它是 User 类型,当你输入 . 时,它会提示 name, age 等属性(如果使用了 Extension),或者通用的 has(...) 方法。

使用 expect

使用 check

编译/运行

IDE 提示类型安全

Failure Message

编写测试

Matcher 风格

Fluent 风格

Result

清晰的错误树

二、集成与用法详解

2.1 添加依赖

通常作为 dev_dependencies

dev_dependencies:
  test: any # 或者 flutter_test
  checks: ^0.3.1

2.2 基础用法

import 'package:test/test.dart';
import 'package:checks/checks.dart';

void main() {
  test('List check', () {
    final list = [1, 2, 3];
    
    // 链式调用
    check(list)
      ..hasLength(3)
      ..contains(2)
      ..not(it()..contains(4)); // 否定断言
  });

  test('Future check', () async {
    final future = Future.value(42);
    // 异步检查
    await check(future).completes(it()..equals(42));
  });
}

在这里插入图片描述

2.3 高级用法:检查对象属性

这需要一些额外工作(定义 Extension),但收益巨大。

假设我们有:

class User {
  final String name;
  final int age;
  User(this.name, this.age);
}

我们可以为检查库定义扩展:

extension UserChecks on Subject<User> {
  Subject<String> get name => has((u) => u.name, 'name');
  Subject<int> get age => has((u) => u.age, 'age');
}

现在你可以这样写:

check(user)
  ..name.equals('张三')
  ..age.isGreaterThan(18);

2.4 异步流检查

检查 Stream 从未如此简单。

test('Stream check', () async {
  final stream = Stream.fromIterable([1, 2, 3]);

  await check(stream).emitsInOrder([
    it()..equals(1),
    it()..equals(2),
    it()..equals(3),
  ]);
});

三、OpenHarmony 适配与实战:复杂业务逻辑验证

在开发鸿蒙应用时,我们经常需要验证很深的数据结构,比如从数据库读取的复杂配置对象。

3.1 深度嵌套验证 (Deep Validation)

使用 checks,我们可以 drill down (钻取) 到对象的深层属性进行验证,如果失败,错误信息会清晰地指出是哪一层的哪个属性不匹配。

// 假设 config.network.proxy.port
check(config).has((c) => c.network, 'network')
  .has((n) => n.proxy, 'proxy')
  .has((p) => p.port, 'port')
  .equals(8080);

报错信息示例:

Expected: a Config that:
  has network that:
    has proxy that:
      has port that:
        equals <8080>
Actual: a Config that:
  has network that:
    has proxy that:
      has port that:
        Example: <9090> // 清晰指出实际值

3.2 软断言 (Soft Assertions)

虽然 checks 目前主要还是 Fail-fast 模式,但通过链式调用(Cascade operator ..),我们可以在一次检查中验证多个属性,而不是分多行写,这样代码结构更紧凑。

四、迁移策略

如果你的项目已经有几千个 expect,完全没必要全部重写。
checks 可以与 expect 共存。建议:

  1. 新测试:全面使用 checks
  2. 复杂测试重构:如果某个旧测试很难读懂,或者报错信息让人摸不着头脑,尝试用 checks 重写它。

五、总结

checks 是 Dart 测试体验的一次重大升级。它从其他语言(如 Java 的 AssertJ)借鉴了优秀的 Fluent API 设计。

对于 OpenHarmony 开发者,拥抱 checks 意味着更高效的 TDD(测试驱动开发)流程。当你面对鸿蒙系统复杂的异步事件和数据状态时,一个清晰、类型安全的断言库能帮你在这片混沌中建立起坚固的防线。

最佳实践

  1. 多写 Extension:为你项目中的核心 Domain Model(用户、订单、配置)编写专门的 Subject 扩展,这哪怕花点时间,也能在后续编写数百个测试用例时赚回来。
  2. 利用 Cascade:用 .. 来组合对于同一个对象的多个断言。

六、完整实战示例

import 'package:test/test.dart';
import 'package:checks/checks.dart';

// 1. 被测对象
class User {
  final String name;
  final int age;
  final List<String> roles;
  User(this.name, this.age, this.roles);
}

// 2. 编写扩展以便享受智能提示
extension UserChecks on Subject<User> {
  Subject<String> get name => has((u) => u.name, 'name');
  Subject<int> get age => has((u) => u.age, 'age');
  Subject<List<String>> get roles => has((u) => u.roles, 'roles');
}

void main() {
  test('User 验证测试', () {
    final user = User('张三', 25, ['admin', 'editor']);

    // 3. 使用 Fluent API 进行断言
    check(user)
      ..name.equals('张三')
      ..name.not(it()..contains('李')) // 名字不包含'李'
      ..age.isGreaterThan(18)
      ..roles.contains('admin')
      ..roles.hasLength(2);
  });

  test('异步流测试', () async {
    final stream = Stream.fromIterable([1, 2, 3]);
    
    // 验证流的顺序和值
    await check(stream).emitsInOrder([
      it()..equals(1),
      it()..isGreaterThan(1), // 2 > 1
      it()..equals(3),
    ]);
  });
}

在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐