Stream中列表内容一致但哈希码不同,Flutter测试如何通过?
流内容相等性校验问题解决方案
问题描述
我需要测试以下watchDebtPaybackList方法(返回Stream<Either<LoadDataFailure, List<DebtPaybackItem>>>):
@override Stream<Either<LoadDataFailure, List<DebtPaybackItem>>> watchDebtPaybackList({ required int personId, required int limit, DateFilter dateFilter = DateFilter.all, }) async* { try { yield* _database.debtPaybackList(personId, limit).watch().map((result) { final debtPaybackList = result.map((e) => e.toDomain()).toList(); return right(debtPaybackList); }); } on DriftWrappedException { yield left(const LoadDataFailure.unexpected()); } }
编写的测试代码如下:
test('watchDebtPaybackList', () async { // Arrange final value = DebtPaybackData( id: 1, personId: 1, amount: 10, date: DateTime.now(), isDebt: 1, ); when(() => mockDatabase.debtPaybackList(any(), any())).thenAnswer((_) { return MockSelectableData<DebtPaybackData>([value]); }); // Act final stream = sud.watchDebtPaybackList(personId: 1, limit: 10); // Assert final exception = right<LoadDataFailure, List<DebtPaybackItem>>( [value.toDomain()], ); print('$exception == $stream: ${exception.hashCode} == ${stream.hashCode}'); expect( stream, emits(exception), ); });
测试始终失败,尽管流输出的列表内容与预期一致,但错误提示显示二者哈希码不同。尝试将Stream转为Future并使用equal匹配仍无效,请问如何忽略哈希码进行相等性校验?
解决方案
问题核心是默认的对象相等性校验依赖引用地址而非内容。以下是三种可行的解决方式:
1. 为DebtPaybackItem实现内容相等性
在DebtPaybackItem类中重写==运算符和hashCode,让校验基于对象的实际字段:
class DebtPaybackItem { final int id; final int personId; final double amount; final DateTime date; final bool isDebt; DebtPaybackItem({ required this.id, required this.personId, required this.amount, required this.date, required this.isDebt, }); @override bool operator ==(Object other) => identical(this, other) || other is DebtPaybackItem && runtimeType == other.runtimeType && id == other.id && personId == other.personId && amount == other.amount && date.isAtSameMomentAs(other.date) && isDebt == other.isDebt; @override int get hashCode => Object.hash(id, personId, amount, date, isDebt); }
这样List<DebtPaybackItem>和Either的相等性都会基于内容判断,原测试代码的expect语句就能正常通过。
2. 使用predicate手动校验流内容
如果不想修改实体类,直接在测试中用predicate自定义校验逻辑:
expect( stream, emits(predicate<Either<LoadDataFailure, List<DebtPaybackItem>>>((event) { return event.fold( (failure) => false, // 失败分支不符合预期 (items) { if (items.length != 1) return false; final expectedItem = value.toDomain(); final actualItem = items.first; return actualItem.id == expectedItem.id && actualItem.personId == expectedItem.personId && actualItem.amount == expectedItem.amount && actualItem.date.isAtSameMomentAs(expectedItem.date) && actualItem.isDebt == expectedItem.isDebt; }, ); })), );
3. 借助ListEquality校验列表
导入package:matcher,用ListEquality直接对比列表内容:
import 'package:matcher/matcher.dart'; // ... expect( stream, emits(right(predicate<List<DebtPaybackItem>>((items) { return const ListEquality().equals(items, [value.toDomain()]); }))), );
注意:校验
DateTime时必须用isAtSameMomentAs,避免因毫秒/微秒差异导致校验失败。
内容的提问来源于stack exchange,提问作者Mahmood Ali
相关产品推荐
相关产品推荐

