1

我目前正在尝试为项目构建小部件测试(专注于 TDD)。我正在使用 Injectable 来生成依赖注入以及在各种实现之间交换的环境。我遇到的问题是我无法@GenerateMocks([IAuthRepository])在单元测试中使用类似的东西,因此需要将模拟注入 GetIt。

@Environment("test")
@LazySingleton(as: IAuthRepository)
class MockFirebaseAuthFascade with Mock implements IAuthRepository {}

在我的小部件测试中,我首先注入文本环境,然后用于getIt<IAuthRepository>()调用模拟实例,以便我可以在测试中使用whenverify

void main() {
  setUpAll(() async {
    configureInjection(Environment.test);
  });

testWidgets(
        'GIVEN form is initial state '
        'WHEN ElevatedButton is pressed '
        'THEN see two error messaged below TextField', (tester) async {
      // Arrange
      await tester.pumpWidget(
        MaterialApp(
          localizationsDelegates: const [
            Localize.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: Localize.delegate.supportedLocales,
          home: BlocProvider(
            create: (context) => getIt<SignInFormBloc>(),
            child: const Scaffold(
              body: SignInForm(),
            ),
          ),
        ),
      );
      final mockAuth = getIt<IAuthRepository>();
      final Finder signIn =
          find.byKey(const Key("auth_sign_in_with_email_button"));
      // Act
      await tester.pumpAndSettle();
      await tester.tap(signIn);
      await tester.pump(const Duration(seconds: 1));
      // Assert
      verifyNever(mockAuth.resetPassword(email: anyNamed("named")));
  });
}

我得到的是一个错误,anyNamed("named")因为它只是被嘲笑,而不是@GenerateMocks([IAuthRepository])在我的单元测试中使用。我不明白,或者找不到答案,我应该如何使用@GenerateMocksInjectable?还是有另一种方法可以为我的测试环境生成更好的模拟?

任何指导或建议都非常受欢迎。

4

1 回答 1

1

经过一番思考和一些黑客攻击,我想出了一个解决方法。似乎没有其他人有更好的想法,所以如果其他人有这个问题,那么这就是我现在正在做的事情:

TLDR;是我@GenerateMocks([IAuthRepository])在我的测试环境中使用然后extend MockIAuthRepository而不是with mock最后我必须在测试中使用 getIt 实例MockIAuthRepository

文件结构

├── lib
│   └── infrastructure
│       └── auth
│           ├── firebase_repository.dart
│           ├── mock_firebase_repository.dart
│           └── mock_firebase_repository.mocks.dart
└── test
    └── presentation
        └── auth
            └── login_form_test.dart

mock_firebase_repository.dart

@Environment("test")
@LazySingleton(as: IAuthRepository)
@GenerateMocks([IAuthRepository])
class MockFirebaseAuthRepository extends MockIAuthRepository implements IAuthRepository {}

login_form_test.dart

void main() {
  setUpAll(() async {
    configureInjection(Environment.test);
  });

  testWidgets(
        'GIVEN form is filled in correctly '
        'WHEN LoginButon is pressed '
        'THEN see no errors', (tester) async {
      // Arrange
      final mock = getIt.get<IAuthRepository>() as MockIAuthRepository;  <-- here is magic!
      when(mock.signInWithEmailAndPassword(
        email: anyNamed("email"),
        password: anyNamed("password"),
      )).thenAnswer((_) async => const Right(unit));
      await tester.pumpWidget(
        MaterialApp(
          localizationsDelegates: const [
            Localize.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: Localize.delegate.supportedLocales,
          home: BlocProvider(
            create: (context) => getIt<LoginFormBloc>(),
            child: const Scaffold(
              body: LoginForm(),
            ),
          ),
        ),
      );
      final Finder emailField = find.byKey(keyAuthEmailField);
      final Finder passwordField = find.byKey(keyAuthPasswordField);
      final Finder loginButton = find.byKey(keyAuthLoginButton);
      // Act
      await tester.pumpAndSettle();

      await tester.enterText(emailField, "test@ing.com");
      await tester.enterText(passwordField, "Passw0rd");
      await tester.pumpAndSettle();

      await tester.tap(loginButton);
      await tester.pump(const Duration(seconds: 1));
      // Assert
      List<TextField> formFields = [];
      find.byType(TextField).evaluate().toList().forEach((element) {
        formFields.add(element.widget as TextField);
      });
      for (var element in formFields) {
        expect(element.decoration?.errorText, isNull);
      }
      expect(find.byKey(keyAuthErrorSnackbar), findsNothing);
      expect(find.byKey(keyAuthSuccessSnackbar), findsOneWidget);
    });
}

这不是最漂亮的解决方案,因为我认为我不需要将类型转换为MockIAuthRepository,但它有效。我最终在我的基础设施中生成了 Mock 以及模拟注入和 prod 注入,因此有一些额外的样板,但至少我不需要在设置完成后触摸模拟并且可以很容易地交换它们做测试。

希望这对其他人有所帮助,并使他们免于在一周内将头撞在键盘上。

于 2021-10-11T13:40:47.127 回答