为何单测在GUI单独执行通过,Flutter CLI执行却失败?
问题分析:JsonSerializable必填字段测试异常
我正在为包含可选参数的对象编写测试,验证其初始化规则,类定义如下:
part 'example.g.dart'; @JsonSerializable(createToJson: false) class Example { String req1; String req2; String? opt; Example({required this.req1, required this.req2, this.opt}); factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json); }
对应的自动生成的example.g.dart代码为:
Example _$ExampleFromJson(Map<String, dynamic> json) { return Example( req1: json['req1'] as String, req2: json['req2'] as String, opt: json['opt'] as String?, ); }
编写了以下两个测试用例,在Android Studio GUI中单独执行可通过,但使用flutter test命令行执行时失败:
test('should fail when json without req1 is provided', () { var testJson = { 'req2': 'a-req2', }; expect(() => Example.fromJson(testJson), throwsA(isA<TypeError>())); }); test('should fail when json without req2 is provided', () { var testJson = { 'req1': 'a-req1', }; expect(() => Example.fromJson(testJson), throwsA(isA<TypeError>())); });
Android Studio与终端的Dart版本不同(2.10和2.17.6),但在与GUI同版本的CI环境中执行测试也失败。管道执行时的错误信息如下:
Expected: throws <Instance of 'TypeError'> Actual: <Closure: () => Example> Which: returned <Instance of 'Example'>
即即使必填字段缺失,对象仍被实例化,请问这是什么原因?
解决方案
核心原因
- 类型转换的行为差异:当
json['req1']不存在时返回null,执行null as String在不同运行模式下表现不同:- Android Studio的测试通常运行在debug模式,类型检查严格,会直接抛出
TypeError; flutter test默认运行在优化后的模式,Dart的类型转换被弱化,null as String不会立即触发报错,而是将null赋值给非空的String字段,导致对象被错误实例化。
- Android Studio的测试通常运行在debug模式,类型检查严格,会直接抛出
- JsonSerializable默认不校验字段存在性:自动生成的代码仅做类型转换,不会检查必填字段是否存在于JSON中,缺失字段时的
null赋值在宽松模式下不会触发异常。
修复步骤
启用JsonSerializable的严格校验:在
@JsonSerializable注解中添加disallowUnrecognizedKeys: true,并为必填字段添加@JsonKey(required: true)标记:@JsonSerializable(createToJson: false, disallowUnrecognizedKeys: true) class Example { @JsonKey(required: true) String req1; @JsonKey(required: true) String req2; String? opt; Example({required this.req1, required this.req2, this.opt}); factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json); }重新生成代码后,
fromJson会自动校验必填字段是否存在,缺失时会抛出MissingRequiredKeysException。修改测试用例的断言目标:不再断言
TypeError,改为断言MissingRequiredKeysException:import 'package:json_annotation/json_annotation.dart'; test('should fail when json without req1 is provided', () { var testJson = { 'req2': 'a-req2', }; expect(() => Example.fromJson(testJson), throwsA(isA<MissingRequiredKeysException>())); });统一测试运行模式:如果需要保持严格的类型检查,可以在运行测试时强制开启debug模式:
flutter test --debug
内容的提问来源于stack exchange,提问作者gabrielrf97
相关产品推荐
相关产品推荐

