单元测试 GetxController
Unit Testing GetxController
我是 tdd 的初学者,所以如果这是一个愚蠢的问题,请原谅我。
我在对 GetxController 进行单元测试时遇到困难。有谁知道这样做的简单方法?
每当我这样做时,我都会收到错误消息,因为 Get 正在调用 onStart 并且它不喜欢 Mockito 给出的结果。我已经尝试使用 Mockito 5.0.1 的自动生成代码以及旧语法,class MockController extends Mock implements Controller{},以及 extends 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);
}
尝试将您的假控制器定义更改为:
class FakeAuthController extends GetxController with Fake implements AuthController {}
不确定这是否适用于 Fake
,但我刚刚用它解决了 Mock
的类似问题。
此问题现已在 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 或 mocktail
如果你需要模拟你的 GetxController/GetxService,你应该扩展 GetxController,然后将它与 Mock 混合,这样
class NotificationServiceMock extends GetxService with Mock implements NotificationService {}
不太直观,是吗?
我是 tdd 的初学者,所以如果这是一个愚蠢的问题,请原谅我。
我在对 GetxController 进行单元测试时遇到困难。有谁知道这样做的简单方法? 每当我这样做时,我都会收到错误消息,因为 Get 正在调用 onStart 并且它不喜欢 Mockito 给出的结果。我已经尝试使用 Mockito 5.0.1 的自动生成代码以及旧语法,class MockController extends Mock implements Controller{},以及 extends 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);
}
尝试将您的假控制器定义更改为:
class FakeAuthController extends GetxController with Fake implements AuthController {}
不确定这是否适用于 Fake
,但我刚刚用它解决了 Mock
的类似问题。
此问题现已在 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 或 mocktail
如果你需要模拟你的 GetxController/GetxService,你应该扩展 GetxController,然后将它与 Mock 混合,这样
class NotificationServiceMock extends GetxService with Mock implements NotificationService {}
不太直观,是吗?