【问题标题】:Injecting generated mock classes into test" environment将生成的模拟类注入“测试”环境
【发布时间】:2021-09-28 12:03:16
【问题描述】:

我目前正在尝试为项目构建小部件测试(专注于 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])。我不明白,或者找不到答案,我应该如何将@GenerateMocks 与 Injectable 一起使用?还是有另一种方法可以为我的测试环境生成更好的模拟?

非常感谢任何指导或建议。

【问题讨论】:

    标签: flutter unit-testing dart mockito injectable


    【解决方案1】:

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

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

    文件结构

    ├── 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-04-09
      • 2018-06-23
      • 1970-01-01
      • 2018-11-23
      • 1970-01-01
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多