【问题标题】:Unit tests with mocktail pass but 'Null' is not a subtype of type 'Future<>' runtime exception is thrown带有 mocktail 的单元测试通过但“Null”不是“Future<>”类型的子类型引发运行时异常
【发布时间】:2021-12-08 16:04:50
【问题描述】:

有类似的问题询问相同的错误(例如here),但它们的原因是由于不正确的模拟。就我而言,我似乎正确地模拟了该方法,但是当我在启用 All Exceptions 的 Visual Studio Code 中进行调试时,我得到了运行时异常:

_TypeError (type 'Null' is not a subtype of type 'Future<AuthenticationToken?>')

如果我在异常之后继续测试(或者只是在禁用 All Exceptions 的情况下调试测试,或者只是在不调试的情况下运行它们),我的所有测试都可以通过。

dependencies:
  hive: ^2.0.4
  hive_flutter: ^1.1.0

dev_dependencies:
  mocktail: ^0.1.4
import 'package:hive/hive.dart';

class AuthenticationRepository {
  static const _currentTokenKey = 'key';
  AuthenticationToken? _inMemoryToken;
  Future<Box<AuthenticationToken?>> _tokenBox;
  ...
  Future<AuthenticationToken?> activeToken() async =>
      _inMemoryToken ?? (await _tokenBox).get(_currentTokenKey);
  ...
}

示例测试文件:

import 'package:app/src/data/authentication/repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockAuthenticationRepository extends Mock
    implements AuthenticationRepository {}

void main() {
  AuthenticationRepository authenticationRepository;
  SUT sut; // SUT depends on AuthenticationRepository

  setUp(() {
    authenticationRepository = MockAuthenticationRepository();

    when(() => authenticationRepository.activeToken())
      .thenAnswer((realInvocation) => Future.value(AuthenticationToken()));
    
    sut = SUT(authenticationRepository);
  });

  test('some test', () async {
    await sut.someMethod();
    verify(() => authenticationRepository.activeToken()).called(1);
  });
}

这是堆栈跟踪:

MockAuthenticationRepository.activeToken (/Users/davilin/Documents/Projects/app/flutter/app/lib/src/data/authentication/repository.dart:296)
main.initMocks.<anonymous closure> (/Users/davilin/Documents/Projects/app/flutter/app/test/network/token_refresh_interceptor_test.dart:33)
when.<anonymous closure> (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/mocktail-0.1.4/lib/src/mocktail.dart:211)
main.initMocks (/Users/davilin/Documents/Projects/app/flutter/app/test/network/token_refresh_interceptor_test.dart:33)
main.<anonymous closure>.<anonymous closure> (/Users/davilin/Documents/Projects/app/flutter/app/test/network/token_refresh_interceptor_test.dart:52)
Declarer._runSetUps.<anonymous closure> (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/src/backend/declarer.dart:329)
Future.forEach.<anonymous closure> (dart:async/future.dart:495)
Future.doWhile.<anonymous closure> (dart:async/future.dart:535)
StackZoneSpecification._registerUnaryCallback.<anonymous closure>.<anonymous closure> (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/stack_zone_specification.dart:126)
StackZoneSpecification._run (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/stack_zone_specification.dart:208)
StackZoneSpecification._registerUnaryCallback.<anonymous closure> (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/stack_zone_specification.dart:126)
_rootRunUnary (dart:async/zone.dart:1436)
_CustomZone.runUnary (dart:async/zone.dart:1335)
_CustomZone.runUnaryGuarded (dart:async/zone.dart:1244)
_CustomZone.bindUnaryCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1281)
Future.doWhile (dart:async/future.dart:551)
Future.forEach (dart:async/future.dart:493)
Declarer._runSetUps (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/src/backend/declarer.dart:329)
<asynchronous gap> (Unknown Source:0)
StackZoneSpecification._registerUnaryCallback.<anonymous closure> (/Users/davilin/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/stack_zone_specification.dart:0)
<asynchronous gap> (Unknown Source:0)

我只是想记录一下,以防有人能解释为什么会发生这种情况。

【问题讨论】:

  • 你可以分享这个功能吗? .get(_currentTokenKey)?
  • @pedropimont 是的,它来自 hive (2.0.4),box.dart。源代码在这里:github.com/hivedb/hive/blob/master/hive/lib/src/box/box.dart
  • 您使 AuthenticationToken 可以为空,因此您的代码可以处理未来的空值。问题不在这里。问题是你没有得到未来,你得到的是空值。我不确定是什么导致了问题,但它可能与 _inMemoryToken 的类型有关。如果 _inMemoryToken 不是未来,编译器可能会感到困惑。
  • @EmreSURK 感谢您的建议。为了测试,我让 activeToken() 返回一个常量非空值或 Future,但它们仍然会出现错误:(抱歉,注释格式难以阅读):1)Future activeToken() => Future(() => const AuthenticationToken()); 2) Future activeToken() async => const AuthenticationToken(); 3) 未来 activeToken() => _activeToken(); Future _activeToken() async => _inMemoryToken ?? (等待_tokenBox).get(_currentTokenKey);
  • 怎么样:Future

标签: flutter dart exception null mocking


【解决方案1】:

我跟进了 mocktail 的作者here,这是他的解释:

这是按预期工作的。问题是您启用了“所有异常”,而不仅仅是“未捕获的异常”。在内部,mocktail 会处理捕获类型错误,因此您只需取消选中“所有异常”即可。

希望对你有所帮助?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-26
    • 2020-06-14
    • 1970-01-01
    • 2020-10-13
    • 2020-07-08
    • 2020-11-17
    • 1970-01-01
    • 2019-11-10
    相关资源
    最近更新 更多