单元测试 GetxController

14

我是一名tdd初学者,如果问题很蠢,请原谅。

我在对GetxControllers进行单元测试时遇到了困难。有没有人知道一个简单的方法来做到这一点?每次尝试时都会出现错误,因为Get正在调用onStart,并且它不喜欢Mockito给它的结果。我尝试使用Mockito 5.0.1的自动生成代码以及旧语法、类MockController扩展Mock实现Controller{},以及扩展Fake。

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

此外,我可以看到get_test包,但其中的文档基本上是0,在示例中控制器仅直接使用--从未使用模拟控制器。

我尝试设置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);
  }
}

和自动生成的有缺陷的模拟控制器:

// 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);
}

我也遇到了同样的问题!你在这个问题上有进展吗?如果你愿意,可以通过电报联系我:https://t.me/maurodibert - Mau Di Bert
2个回答

3

这个问题现在已经在GetX文档中得到了解答。

从文档中复制粘贴如下:

测试

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

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或mocktail

如果你需要模拟你的GetxController/GetxService,你应该扩展GetxController,并将其混合使用Mock,这样

class NotificationServiceMock extends GetxService with Mock implements NotificationService {}

这并不是非常直观,不是吗?


如何使用代码生成的注释来完成这个任务? - King Leon

0

尝试将您的虚拟控制器定义更改为:

class FakeAuthController extends GetxController with Fake implements AuthController {}

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接