测试emitArray方法时Response.Success包裹列表后断言失败问题排查
解决Stream测试中包裹List的SuccessResponse相等性校验失败问题
你的问题核心确实出在SuccessResponse的==运算符实现上,具体来说是Dart中List的默认相等性判断逻辑导致的。
问题根源分析
你在测试代码中创建的Response.success(['test']),和emitArray方法里创建的Response.success(['test']),看起来内容完全一致,但它们的value是两个完全独立的List实例。Dart中List的默认==运算符是比较引用地址,而不是内容是否相同。你的SuccessResponse的==直接复用了value == other.value,这就导致两个看似相同的SuccessResponse实例会被判定为不相等,最终让expect断言失败。
两种可行的解决方案
方案一:修改SuccessResponse的相等性判断逻辑(通用型)
如果希望SuccessResponse能正确处理集合类型(List、Map、Set等)的内容相等性,可以借助collection包中的相等性工具类来实现:
- 首先在
pubspec.yaml中添加collection依赖:
dependencies: collection: ^1.17.2 # 可以使用最新稳定版本
- 修改
SuccessResponse的==和hashCode实现:
import 'package:collection/collection.dart'; class SuccessResponse<T> extends Response<T> { SuccessResponse(this.value): super._(); final T value; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is SuccessResponse<T> && runtimeType == other.runtimeType && _areValuesEqual(value, other.value); } bool _areValuesEqual(T a, T b) { if (a is List && b is List) return const ListEquality().equals(a, b); if (a is Map && b is Map) return const MapEquality().equals(a, b); if (a is Set && b is Set) return const SetEquality().equals(a, b); // 其他自定义类型可以在这里扩展判断逻辑 return a == b; } @override int get hashCode { if (value is List) return const ListEquality().hash(value); if (value is Map) return const MapEquality().hash(value); if (value is Set) return const SetEquality().hash(value); return value.hashCode; } @override String toString() { return 'SuccessResponse{value: $value}'; } }
这种方式让SuccessResponse对常见集合类型的相等性判断更符合直觉,不仅能解决当前测试的问题,后续其他场景使用也不会踩同样的坑。
方案二:在测试中使用内容匹配器(无需修改原代码)
如果不想改动SuccessResponse的实现,可以在测试里用test包提供的equals匹配器(它会递归比较对象内容,而非依赖==运算符),或者自定义断言逻辑:
void main() { ListResponse underTest; setUp(() { underTest = ListResponse(); }); test('It should emit array', () { expect( underTest.array, emitsInOrder([ // 使用equals匹配器判断内容相等 equals(Response.success(['test'])), emitsDone, ]), ); underTest.emitArray(); underTest.dispose(); }); }
或者用更灵活的predicate来做自定义校验:
test('It should emit array', () { expect( underTest.array, emitsInOrder([ predicate<SuccessResponse<List<String>>>((res) { return res.value.isNotEmpty && res.value.first == 'test'; }, 'emits a SuccessResponse containing ["test"]'), emitsDone, ]), ); underTest.emitArray(); underTest.dispose(); });
验证效果
无论选择哪种方案,重新运行测试后,断言都会正确识别两个SuccessResponse实例的内容相等性,测试就能顺利通过了。
内容的提问来源于stack exchange,提问作者Juan Cruz Soler
相关产品推荐
相关产品推荐

