简体   繁体   English

单元测试 GetxController

[英]Unit Testing GetxController

I'm a beginner with tdd so please forgive me if it's a dumb question.我是 tdd 的初学者,所以如果这是一个愚蠢的问题,请原谅我。

I'm having difficulty unit testing GetxControllers.我在对 GetxControllers 进行单元测试时遇到困难。 Does anyone know a simple way of doing this?有谁知道这样做的简单方法? Whenever I do I get errors since Get is calling onStart and it doesn't like the result Mockito's giving it.每当我这样做时,我都会收到错误,因为 Get 正在调用 onStart 并且它不喜欢 Mockito 给出的结果。 I've tried using Mockito 5.0.1's auto generated code as well as the older syntax, class MockController extends Mock implements Controller{}, as well as extends Fake.我试过使用 Mockito 5.0.1 的自动生成代码以及旧语法 class MockController extends Mock implements Controller{},以及扩展 Fake。

The auto generated code has build errors, since Mockito is trying to use _InternalFinalCallback, but it's not being imported as it's private.自动生成的代码有构建错误,因为 Mockito 正在尝试使用 _InternalFinalCallback,但它没有被导入,因为它是私有的。 I tried just copy pasting that part of the code into my generated file (and switching off pub build watch) but first that's a short term solution with it's own issues, 2nd it still doesn't work since the onStart and onDelete functions now tell me they're not valid overrides.我尝试只是将那部分代码复制粘贴到我生成的文件中(并关闭 pub build watch)但首先这是一个短期解决方案,它有自己的问题,第二它仍然不起作用,因为 onStart 和 onDelete 函数现在告诉我它们不是有效的覆盖。

Also, I can see the get_test package but it's documentation is basically 0, and in the examples the controller is just used directly -- there's never a mocked controller.此外,我可以看到 get_test package 但它的文档基本上是 0,并且在示例中 controller 只是直接使用 - 从来没有一个模拟的 Z594C103F2C6E04C03D8AB059F03。

I tried setting Get.testMode = true;我尝试设置 Get.testMode = true; but again that doesn't seem to do anything.但这似乎没有任何作用。 And while I found that property in the docs, I didn't find how to use it correctly.虽然我在文档中找到了该属性,但我没有找到正确使用它的方法。

Any help would be appreciated,任何帮助,将不胜感激,

Here's my code but the issue seems to be with the GetxControllers, so I don't think it's relevant much:这是我的代码,但问题似乎出在 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());
    });
  });
}

There really isn't anything in my AuthController and session controller, but code is as follows:我的 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);
  }
}

And the auto-generated, buggy mock controller:以及自动生成的错误模拟 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);
}

This question has now been answered in the GetX docs.这个问题现已在 GetX 文档中得到解答。

Pasted from the docs:从文档粘贴:

Tests测试

You can test your controllers like any other class, including their lifecycles:您可以像任何其他 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 or mocktail Mockito 或无酒精鸡尾酒

If you need to mock your GetxController/GetxService, you should extend GetxController, and mixin it with Mock, that way如果你需要模拟你的 GetxController/GetxService,你应该扩展 GetxController,并将它与 Mock 混合,这样

class NotificationServiceMock extends GetxService with Mock implements NotificationService {}

Not exactly intuitive, is it?不完全直观,是吗?

Try changing your fake controller definition to:尝试将您的假 controller 定义更改为:

class FakeAuthController extends GetxController with Fake implements AuthController {}

Not sure this works for Fake , but I just fixed a similar issue with Mock with it.不确定这是否适用于Fake ,但我刚刚修复了与Mock类似的问题。

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

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