繁体   English   中英

单元测试 GetxController

[英]Unit Testing GetxController

我是 tdd 的初学者,所以如果这是一个愚蠢的问题,请原谅我。

我在对 GetxControllers 进行单元测试时遇到困难。 有谁知道这样做的简单方法? 每当我这样做时,我都会收到错误,因为 Get 正在调用 onStart 并且它不喜欢 Mockito 给出的结果。 我试过使用 Mockito 5.0.1 的自动生成代码以及旧语法 class MockController extends Mock implements Controller{},以及扩展 Fake。

自动生成的代码有构建错误,因为 Mockito 正在尝试使用 _InternalFinalCallback,但它没有被导入,因为它是私有的。 我尝试只是将那部分代码复制粘贴到我生成的文件中(并关闭 pub build watch)但首先这是一个短期解决方案,它有自己的问题,第二它仍然不起作用,因为 onStart 和 onDelete 函数现在告诉我它们不是有效的覆盖。

此外,我可以看到 get_test package 但它的文档基本上是 0,并且在示例中 controller 只是直接使用 - 从来没有一个模拟的 Z594C103F2C6E04C03D8AB059F03。

我尝试设置 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 文档中得到解答。

从文档粘贴:

测试

您可以像任何其他 class 一样测试您的控制器,包括它们的生命周期:

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 {}

不完全直观,是吗?

尝试将您的假 controller 定义更改为:

class FakeAuthController extends GetxController with Fake implements AuthController {}

不确定这是否适用于Fake ,但我刚刚修复了与Mock类似的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM