'Null' 不是类型 'Future<Either<Failure, NumberTrivia>>' 的子类型

'Null' is not a subtype of type 'Future<Either<Failure, NumberTrivia>>'

我正在按照以下视频在 TDD Clean Architecture 中实施 flutter 项目:https://www.youtube.com/watch?v=lPkWX8xFthE&t=1s

我的代码:

class MockNumberTriviaRepository extends Mock
    implements NumberTriviaRepository {}

void main() {
  late MockNumberTriviaRepository mockNumberTriviaRepository;
  late GetConcreteNumberTrivia usecase;

  setUp(() {
    mockNumberTriviaRepository = MockNumberTriviaRepository();
    usecase = GetConcreteNumberTrivia(mockNumberTriviaRepository);
  });

  final tNumber = 1;
  final tNumberTrivia = NumberTrivia(text: 'test', number: 1);

  test(
    'should get trivia for the number from the repository',
    () async {
      //arrange
      when( mockNumberTriviaRepository.getConcreteNumberTrivia(1)).thenAnswer((_) async {
        return Right(tNumberTrivia);
      });

      //act
      final result = await usecase.execute(number: 10);

      //assert
      expect(result, Right(tNumberTrivia));
      verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber));
      verifyNoMoreInteractions(mockNumberTriviaRepository);
    },
  );
} 

当 运行 测试时,结果总是

package:number_trivia/features/number_trivia/domain/repositories/number_trivia_repository.dart 7:41  MockNumberTriviaRepository.getConcreteNumberTrivia
test\features\number_trivia\domain\usecases\get_concrete_number_trivia_test.dart 27:40               main.<fn>
test\features\number_trivia\domain\usecases\get_concrete_number_trivia_test.dart 25:5                main.<fn>

type 'Null' is not a subtype of type 'Future<Either<Failure, NumberTrivia>>'

这里显示错误:

abstract class NumberTriviaRepository{
  Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number);
}
class GetConcreteNumberTrivia {
 final NumberTriviaRepository repository;

 GetConcreteNumberTrivia(this.repository);

 Future<Either<Failure, NumberTrivia>> execute({required int number}) async {
   return await repository.getConcreteNumberTrivia(number);
 }
}

请给我一些继续前进的解决方案。

它 returns 为 null,而您期待其他东西。你必须找到原因,为什么它 returns 为 null。

我在学习教程时遇到了同样的问题。问题来自 Dart 的空安全类型系统。因此,mockito 到存根方法的机制无法正常工作。基本上,您需要一个支持不可空类型的模拟 class。

有两种方法:

  1. 使用 build_runner 包生成 mock class 或
  2. 手动实现模拟class

我遵循了第一种方法,它对我有用。只需在 dev_dependencies 下的 pubspec.yaml 中添加 build_runner 并执行 pub get。然后你需要在测试中用 @GenereateMocks([NumberTriviaRepository]).

注释你的主要方法

之后需要在终端中执行命令“flutter pub 运行 build_runner build”(我是直接用Android Studio中的终端)然后就可以build了模拟 class 给你,它可以在与你的测试文件相同的包中找到。模拟的 class 的名称为 MockNumberTriviaRepository。

我在这里找到了这些方法:https://github.com/dart-lang/mockito/blob/master/NULL_SAFETY_README.md

我的测试代码:

@GenerateMocks([NumberTriviaRepository])
void main() {
  late GetConcreteNumberTrivia useCase;
  late MockNumberTriviaRepository mockNumberTriviaRepository;

  setUp(() {
    mockNumberTriviaRepository = MockNumberTriviaRepository();
    useCase = GetConcreteNumberTrivia(mockNumberTriviaRepository);
  });

  final tNumber = 1;`enter code here`
  final tNumberTrivia = NumberTrivia(text: "Test", number: tNumber);
  // and so on like in the tutorial ...

}

我的 dev_dependencies 在 pubspec.yaml:

dev_dependencies:
    flutter_test:
        sdk: flutter
    mockito: ^5.0.15
    build_runner: ^2.1.2

我遇到了同样的错误,我做了一些研究,最终对我有用。

你需要进行“Null”检查,如果你遇到这样的错误。

要使 Dart 将您的代码视为空安全,SDK 约束必须要求具有空安全支持的语言版本。例如,您的 pubspec.yaml 文件可能具有以下限制:

environment:
  sdk: ">=2.12.0 <3.0.0"

您应该通过在类型名称后添加问号 (?) 来为变量赋予可空类型

abstract class NumberTriviaRepository {
    Future<Either<Failure, NumberTrivia>>? getConcreateNumberTrivia(int number);
    Future<Either<Failure, NumberTrivia>>? getRandomNumberTrivia();
}

调用时必须使用“NumberTriviaRepository”中的函数。

class GetConcreateNumberTrivia {
  final NumberTriviaRepository repository;

  GetConcreateNumberTrivia(this.repository);

  Future<Either<Failure, NumberTrivia>?> execute({required int number}) async {
    return await repository.getConcreateNumberTrivia((number));
  }
}

我在下面添加了一些有用的链接。希望你的问题得到解决。

Understanding null safety

Sound null safety

Practice with null safety

这是 ResoCoder TDD 课程空安全版本。

ResoCoder TDD Course (Null-safety)

如果有帮助请告诉我。

这适用于最新的依赖项 (01/16/2022)。 2-3 个错误的完整答案:

已添加:build_runner: ^2.1.7 至 pubspec.yaml

//get_concrete_number_trivia_test

//....
import 'package:mockito/annotations.dart';
class MockNumberTriviaRepository extends Mock implements NumberTriviaRepo {}

@GenerateMocks([NumberTriviaRepo])
void main() {
  late GetConcreteNumberTrivia usecases;
  late MockNumberTriviaRepository mockNumberTriviaRepository;
  late int tNumber;
  late NumberTrivia tNumberTrivia;
  setUp(() {
    mockNumberTriviaRepository = MockNumberTriviaRepository();
    usecases = GetConcreteNumberTrivia(repo: mockNumberTriviaRepository);
    tNumber = 1;
    tNumberTrivia = NumberTrivia(text: 'test', number: 1);
  });

  test('should get a trivia for the number from the repo', () async {
    // arrange
    when(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber))
        .thenAnswer((_) async => Right(tNumberTrivia));
    // act
    final result = await usecases.execute(number: tNumber);
    //assert
    expect(result, Right(tNumberTrivia));
    verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber));
    verifyNoMoreInteractions(mockNumberTriviaRepository);
  });
}

class NumberTriviaRepository:

abstract class NumberTriviaRepository {
  Future<Either<Failure, NumberTrivia>>? getConcreteNumberTrivia(int number);
  Future<Either<Failure, NumberTrivia>>? getRandomNumberTrivia();
}

class GetConcreteNumberTrivia:

class GetConcreteNumberTrivia {
  final NumberTriviaRepository repo;
  GetConcreteNumberTrivia({
    required this.repo,
  });

  Future<Either<Failure, NumberTrivia>?> execute({required int number}) async {
    return await repo.getConcreteNumberTrivia(number);
  }
}