Flutter - Mockito - 在测试中使用异步会产生错误,但使用异步* 可以正常工作吗?

Flutter - Mockito - Using async in testing produces an error but using async* made it work fine?

简而言之,我正在学习 ResoCoder TDD 课程。该课程有点过时,但我很确定可以继续学习并在此过程中进行调整。本课程 GitHub 中的课程和代码不是空安全的。

而且我没有完全按照课程进行。我在这里和那里做了一些更改。

我刚开始学习第 2 集或第 3 集的课程。

auth_usecases_test.dart

import 'package:dartz/dartz.dart';
import 'package:mockito/mockito.dart';
import 'package:storayge/core/auth/domain/entities/local_user.dart';
import 'package:storayge/core/auth/domain/repository/auth_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:storayge/core/auth/domain/usecases/auth_usecases.dart';

class MockAuthRepository extends Mock implements AuthRepository {}

void main() {
  late GetLocalUserDataFromRemote usecase;
  late MockAuthRepository mockAuthRepository;

  late String tUid;
  late LocalUser tLocalUser;

  setUp(() {
    mockAuthRepository = MockAuthRepository();
    usecase = GetLocalUserDataFromRemote(repository: mockAuthRepository);
    tLocalUser = const LocalUser(
      username: 'testUsername',
      email: 'testEmail@Email',
      uid: 'testUid',
    );
    tUid = 'testUid';
  });

  test(
    'should get LocalUser data from the repository',
    () async* {
      // arrange
      when(mockAuthRepository.getLocalUserDataFromRemote(uid: tUid))
          .thenAnswer((_) async => Right(tLocalUser));
      // act
      final result = await usecase(Params(uid: tUid));
      // assert
      expect(result, equals(Right(tLocalUser)));
      verify(mockAuthRepository.getLocalUserDataFromRemote(uid: tUid));
      verifyNoMoreInteractions(mockAuthRepository);
    },
  );
}

这是“工作”代码。如果我将 async* 关键字更改为 async 它会产生此错误:

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

auth_usecases.dart

class GetLocalUserDataFromRemote implements Usecase<LocalUser, Params> {
  final AuthRepository repository;

  GetLocalUserDataFromRemote({required this.repository});

  @override
  Future<Either<Failure, LocalUser>> call(Params params) async {
    return repository.getLocalUserDataFromRemote(uid: params.uid);
  }
}

class Params extends Equatable {
  final String uid;

  Params({required this.uid});

  @override
  List<Object?> get props => [uid];
}

auth_repository.dart

import 'package:dartz/dartz.dart';

import '../../../errors/failures.dart';
import '../entities/local_user.dart';

abstract class AuthRepository {
  Future<Either<Failure, LocalUser>> getLocalUserDataFromRemote({
    required String uid,
  });

  Future<Either<Failure, LocalUser>> signInWithEmailAndPassword({
    required String email,
    required String password,
  });
}

local_user.dart

class LocalUser extends Equatable {
  final String username;
  final String email;
  final String uid;

  const LocalUser({
    required this.username,
    required this.email,
    required this.uid,
  });

  @override
  List<Object?> get props => [username, email, uid];
}

failures.dart

abstract class Failure extends Equatable {
  final List<Object> properties;

  const Failure({required this.properties});

  @override
  List<Object> get props => properties;
}

更改异步标记为何以及如何使其起作用?我被难住了。

编辑:我的包裹目前都是最新的,具有良好的空安全性。除了我使用 0.1.0-nullsafety.1

的 dartz

编辑 2:一些更新。首先,似乎使用 async* 标记使测试通过,但它似乎并没有真正起作用。我的意思是测试通过了,即使它不应该通过。我想我已经找到了解决方案。这当然是我的错。 mockito 文档说 mockito 确实支持空安全,但有一些代码生成。使用自定义模拟 类 等等。我什么都没做,这很愚蠢,我讨厌自己。所以我做了一些,稍后更新。

我认为这是 mockito 的失败。

尝试以这种方式调用您的代码作为 mockito docs 状态:

      // arrange
      when(mockAuthRepository.getLocalUserDataFromRemote(uid: anyNamed('uid')))
          .thenAnswer((_) async => Right(tLocalUser));


好的。我终于找到了答案。是的,这是我自己的愚蠢错误。 基本上 mockito 5.0.0 现在确实支持空安全,但有一些警告。您将需要使用一些代码生成来生成模拟 类。我没有。基本上我所做的是

class MockAuthRepository extends Mock implements AuthRepository {} // remove

@GenerateMocks([AuthRepository]) //new
void main() {
  late GetLocalUserDataFromRemote usecase;
  late MockAuthRepository mockAuthRepository;

  late String tUid;
  late LocalUser tLocalUser;

  setUp(() {
    mockAuthRepository = MockAuthRepository();
    usecase = GetLocalUserDataFromRemote(repository: mockAuthRepository);
    tLocalUser = const LocalUser(
      username: 'testUsername',
      email: 'testEmail@Email',
      uid: 'testUid',
    );
    tUid = 'testUid';
  });

  test(
    'should get LocalUser data from the repository',
    () async {
      // arrange
      when(mockAuthRepository.getLocalUserDataFromRemote(anyNamed('uid')))
          .thenAnswer((_) async => Right(tLocalUser));
      // act
      final result = await usecase(Params(uid: tUid));
      // assert
      expect(result, equals(Right(tLocalUser)));
      verify(mockAuthRepository.getLocalUserDataFromRemote(uid: tUid));
      verifyNoMoreInteractions(mockAuthRepository);
    },
  );
}

并用build runner生成。这很容易。我就是那么笨,没有仔细阅读文档。

我认为这与 BLoC 的流一样有效。你在 mapEventToState 函数中有 async* 并且在那之后你可以更异步