You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Secure Storage单元测试空类型匹配及空检查错误如何解决

问题原因及修复方案

核心问题

  • 你的Authentication类内部硬编码初始化了FlutterSecureStorage实例,没有对外提供依赖注入入口,测试时你创建的MockFlutterSecureStorage实例从未被实际调用,所有打桩逻辑完全不生效。
  • 测试运行时实际调用的是真实的FlutterSecureStorage实例,测试环境无对应的存储值,返回的null触发了类型不匹配、空安全相关报错。
  • 保存token的测试用例中,打桩时传入的value参数和业务代码实际写入的值不匹配,也会导致打桩失效。

修复步骤

第一步:改造Authentication类支持依赖注入

构造函数新增可选的secureStorage参数,默认使用真实的FlutterSecureStorage实例,不影响原有线上业务逻辑,测试时可传入Mock实例替换:

class Authentication {
  final FlutterSecureStorage _secureStorage;
  static const String BEARER_TOKEN = 'BEARER_TOKEN';

  Authentication({FlutterSecureStorage? secureStorage}) 
      : _secureStorage = secureStorage ?? const FlutterSecureStorage();

 Future<OAuthToken> setToken(OAuthTokenResponse token) async {
  assert(token.accessToken != null, "A non null token is required");
  final String jsonToken = json.encode(token.toJson());
  await _secureStorage.write(key: BEARER_TOKEN, value: jsonToken);
  return token;
 }

 Future<OAuthToken?> getToken() async {
  final String? jsonToken = await _secureStorage.read(key: BEARER_TOKEN);
  if (jsonToken == null) {
   return null;
  }
  return OAuthToken(json.decode(jsonToken));
 }

 Future<void> deleteToken() async {
  await _secureStorage.delete(key: BEARER_TOKEN);
 }
}

第二步:修改测试代码注入Mock实例

初始化Authentication时传入你创建的MockFlutterSecureStorage实例,让打桩逻辑生效;同时统一write操作的参数匹配规则,避免打桩失效:

void main() {
 final MockFlutterSecureStorage mockSecureStorage = MockFlutterSecureStorage();
 // 注入Mock实例替换真实存储类
 final Authentication dataSource = Authentication(secureStorage: mockSecureStorage);

 final token = OAuthToken.fromJson(json.decode(fixture('oauth_token_response.json')));

group('getToken', () {
 test('should return OAuthToken from FlutterSecureStorage when there is one the saved', () async {
   // arrange
   when(() => mockSecureStorage.read(key: BEARER_TOKEN)).thenAnswer((_) async => fixture('oauth_token_response.json'));

   // act
   final result = await dataSource.getToken();

   //assert
   verify(() => mockSecureStorage.read(key: BEARER_TOKEN)).called(1);
   expect(result, equals(token));
 });

 test('should return null when is not saved token', () async {
   // arrange
   when(() => mockSecureStorage.read(key: BEARER_TOKEN)).thenAnswer((_) async => null);

   // act
   final result = await dataSource.getToken();

   //assert
   verify(() => mockSecureStorage.read(key: BEARER_TOKEN)).called(1);
   expect(result, isNull);
 });
});

group('saveToken', () {
 test('should call FlutterSecureStorage to save token', () async {
   // arrange
   final expectedJsonString = json.encode(token.toJson());
   when(() => mockSecureStorage.write(key: BEARER_TOKEN, value: expectedJsonString)).thenAnswer((invocation) => Future<void>.value());

   //act
   await dataSource.setToken(token);

   // assert
   verify(() => mockSecureStorage.write(key: BEARER_TOKEN, value: expectedJsonString)).called(1);
 });
});
}

内容的提问来源于stack exchange,提问作者user11820495

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 09:06:10