flutter - 单元测试 GetxController

标签 flutter unit-testing flutter-getx

我是 tdd 的初学者,所以如果这是一个愚蠢的问题,请原谅我。
我在对 GetxControllers 进行单元测试时遇到困难。有谁知道这样做的简单方法?
每当我这样做时,我都会收到错误,因为 Get 正在调用 onStart 并且它不喜欢 Mockito 给出的结果。我尝试过使用 Mockito 5.0.1 的自动生成代码以及旧语法,MockController 类扩展 Mock 实现 Controller{},以及扩展 Fake。
自动生成的代码有构建错误,因为 Mockito 正在尝试使用 _InternalFinalCallback,但它没有被导入,因为它是私有(private)的。我尝试将那部分代码复制粘贴到我生成的文件中(并关闭 pub build watch)但首先这是一个短期解决方案,它有自己的问题,第二它仍然不起作用,因为 onStart 和 onDelete 函数现在告诉我它们不是有效的覆盖。
另外,我可以看到 get_test 包,但它的文档基本上是 0,并且在示例中, Controller 只是直接使用 - 从来没有模拟 Controller 。
我尝试设置 Get.testMode = true;但这似乎没有任何作用。虽然我在文档中找到了该属性,但我没有找到正确使用它的方法。
任何帮助,将不胜感激,
这是我的代码,但问题似乎出在 GetxControllers 上,所以我认为它的相关性不大:

class FakeAuthController extends Fake implements AuthController {}

@GenerateMocks([AuthController])
void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  late MockAuthController mockAuthController;
  late FakeAuthController fakeAuthController;
  late SessionController sessionController;

  setUp(() {
    Get.testMode = true;
    mockAuthController = MockAuthController();
    fakeAuthController = FakeAuthController();
    Get.put<AuthController>(mockAuthController);

    sessionController = SessionController();
  });

  tearDown(() {
    Get.delete<AuthController>();
  });

  group('getSessionInfo', () {
    test('Calls authFacade getSignedInUserId', () async {
      await sessionController.getSessionInfo();

      when(Get.find<AuthController>()).thenReturn(fakeAuthController);

      verify(mockAuthController.getSignedInUserId());
    });
  });
}
我的 AuthController 和 session Controller 中确实没有任何内容,但代码如下:
import 'package:get/get.dart';

class AuthController extends GetxController {
  String getSignedInUserId() {
    // await Future.delayed(Duration(milliseconds: 1));
    return '1';
  }
}


import 'package:get/get.dart';

import '../../auth/controllers/auth_controller.dart';
import '../models/session_info.dart';

class SessionController extends GetxController {
  final AuthController authController = Get.find<AuthController>();

  Rx<SessionInfo> sessionInfo = Rx<SessionInfo>();

  Future<void> getSessionInfo() async {
    // authController.getSignedInUserId();

    // sessionInfo.value = SessionInfo(userId: userId);
  }
}

以及自动生成的、有缺陷的模拟 Controller :
// Mocks generated by Mockito 5.0.1 from annotations
// in smart_locker_controller/test/shared/controllers/session_controller_test.dart.
// Do not manually edit this file.

import 'dart:ui' as _i4;

import 'package:get/get_instance/src/lifecycle.dart' as _i2;
import 'package:get/get_state_manager/src/simple/list_notifier.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1;
import 'package:smart_locker_controller/auth/controllers/auth_controller.dart'
    as _i3;

// ignore_for_file: comment_references
// ignore_for_file: unnecessary_parenthesis

class _Fake_InternalFinalCallback<T> extends _i1.Fake
    implements _i2._InternalFinalCallback<T> {}

/// A class which mocks [AuthController].
///
/// See the documentation for Mockito's code generation for more information.
class MockAuthController extends _i1.Mock implements _i3.AuthController {
  MockAuthController() {
    _i1.throwOnMissingStub(this);
  }

  @override
  int get notifierVersion =>
      (super.noSuchMethod(Invocation.getter(#notifierVersion), returnValue: 0)
          as int);
  @override
  int get notifierMicrotask =>
      (super.noSuchMethod(Invocation.getter(#notifierMicrotask), returnValue: 0)
          as int);
  @override
  bool get hasListeners =>
      (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
          as bool);
  @override
  int get listeners =>
      (super.noSuchMethod(Invocation.getter(#listeners), returnValue: 0)
          as int);
  @override
  _i2._InternalFinalCallback<void> get onStart =>
      (super.noSuchMethod(Invocation.getter(#onStart),
              returnValue: _Fake_InternalFinalCallback<void>())
          as _i2._InternalFinalCallback<void>);
  @override
  _i2._InternalFinalCallback<void> get onDelete =>
      (super.noSuchMethod(Invocation.getter(#onDelete),
              returnValue: _Fake_InternalFinalCallback<void>())
          as _i2._InternalFinalCallback<void>);
  @override
  bool get initialized =>
      (super.noSuchMethod(Invocation.getter(#initialized), returnValue: false)
          as bool);
  @override
  bool get isClosed =>
      (super.noSuchMethod(Invocation.getter(#isClosed), returnValue: false)
          as bool);
  @override
  String getSignedInUserId() =>
      (super.noSuchMethod(Invocation.method(#getSignedInUserId, []),
          returnValue: '') as String);
  @override
  void update([List<Object>? ids, bool? condition = true]) =>
      super.noSuchMethod(Invocation.method(#update, [ids, condition]),
          returnValueForMissingStub: null);
  @override
  void refreshGroup(Object? id) =>
      super.noSuchMethod(Invocation.method(#refreshGroup, [id]),
          returnValueForMissingStub: null);
  @override
  void removeListener(_i4.VoidCallback? listener) =>
      super.noSuchMethod(Invocation.method(#removeListener, [listener]),
          returnValueForMissingStub: null);
  @override
  void removeListenerId(Object? id, _i4.VoidCallback? listener) =>
      super.noSuchMethod(Invocation.method(#removeListenerId, [id, listener]),
          returnValueForMissingStub: null);
  @override
  _i5.Disposer addListener(_i5.GetStateUpdate? listener) =>
      (super.noSuchMethod(Invocation.method(#addListener, [listener]),
          returnValue: () {}) as _i5.Disposer);
  @override
  _i5.Disposer addListenerId(Object? key, _i5.GetStateUpdate? listener) =>
      (super.noSuchMethod(Invocation.method(#addListenerId, [key, listener]),
          returnValue: () {}) as _i5.Disposer);
  @override
  void disposeId(Object? id) =>
      super.noSuchMethod(Invocation.method(#disposeId, [id]),
          returnValueForMissingStub: null);
}

最佳答案

这个问题现已在 GetX 文档中得到解答。
从文档粘贴:
测试
您可以像任何其他类一样测试您的 Controller ,包括它们的生命周期:

class Controller extends GetxController {
  @override
  void onInit() {
    super.onInit();
    //Change value to name2
    name.value = 'name2';
  }

  @override
  void onClose() {
    name.value = '';
    super.onClose();
  }

  final name = 'name1'.obs;

  void changeName() => name.value = 'name3';
}

void main() {
  test('''
Test the state of the reactive variable "name" across all of its lifecycles''',
      () {
    /// You can test the controller without the lifecycle,
    /// but it's not recommended unless you're not using
    ///  GetX dependency injection
    final controller = Controller();
    expect(controller.name.value, 'name1');

    /// If you are using it, you can test everything,
    /// including the state of the application after each lifecycle.
    Get.put(controller); // onInit was called
    expect(controller.name.value, 'name2');

    /// Test your functions
    controller.changeName();
    expect(controller.name.value, 'name3');

    /// onClose was called
    Get.delete<Controller>();

    expect(controller.name.value, '');
  });
}
Mockito 或无酒精鸡尾酒
如果您需要模拟您的 GetxController/GetxService,您应该扩展 GetxController,并将其与 Mock 混合,这样
class NotificationServiceMock extends GetxService with Mock implements NotificationService {}
不完全直观,是吗?

关于flutter - 单元测试 GetxController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66685369/

相关文章:

flutter - Flutter-LinearProgressIndicator中的文本

flutter - (RenderViewport 不支持返回固有维度

flutter - 如何监听TextController内部的变化?

flutter - 如何在flutter中实现这种随机宽度 GridView

visual-studio-code - 如何将 Flutter 与 Genymotion 连接起来?

windows - FVM/Flutter 无法在 Windows 上的非管理员帐户上找到 git

c# - 我如何模拟私有(private)领域?

php - 如何使用 PHPunit 测试抽象类?

python - 如何断言模拟对象中的方法已被调用?

flutter 和getx : update() does not notify GetBuilder