我正在使用mockito在Flutter中编写测试,我想模拟Riverpod的Provider,但是出现错误并且无法解决。
各个版本如下
# pubspec.yaml
environment:
sdk: ^3.5.2
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
freezed_annotation: ^2.4.4
dev_dependencies:
flutter_test:
sdk: flutter
riverpod_generator: ^2.4.3
riverpod_lint: ^2.3.13
freezed: ^2.5.7
mockito: ^5.4.4
这是我们要测试的代码
// notifier_provider_page.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_flutter/user_item_provider.dart';
import 'todo.dart';
part 'notifier_provider_page.g.dart';
@riverpod
class TodoList extends _$TodoList {
@override
List<Todo> build() {
return [];
}
// Methods under test
void add(Todo todo) {
final user = ref.read(userItemProvider);
if (user?.email == 'a') {
state = [...state, todo];
}
}
}
上面代码中使用的userItemProvider如下
// user_item_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'user.dart';
part 'user_item_provider.g.dart';
@riverpod
class UserItem extends _$UserItem {
@override
User build() {
return const User(name: "a", email: "b", password: "c");
}
void add(User user) {
state = user;
}
}
测试代码如下(失败)
// notifier_provider_page_test.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:riverpod_flutter/notifier_provider_page.dart';
import 'package:riverpod_flutter/todo.dart';
import 'package:riverpod_flutter/user_item_provider.dart';
import 'notifier_provider_page_test.mocks.dart';
@GenerateNiceMocks([MockSpec<UserItem>()])
void main() {
late ProviderContainer container;
final MockUserItem mockUserItem = MockUserItem();
setUp(() {
container = ProviderContainer(overrides: [
userItemProvider.overrideWith(() {
return mockUserItem;
})
]);
});
tearDown(() {
container.dispose();
});
test('test', () {
final todo = container.read(todoListProvider.notifier);
todo.add(const Todo(
userId: 1,
id: 1,
title: 'title',
));
expect(container.read(todoListProvider),
[const Todo(userId: 1, id: 1, title: 'title')]);
});
}
出现以下错误
NoSuchMethodError: Class 'MockUserItem' has no instance method '_setElement'.
Receiver: Instance of 'MockUserItem'
Tried calling: _setElement(Instance of 'AutoDisposeNotifierProviderElement<UserItem, User>')
从本页修改的代码如下
// user_item_provider.dart
// The following classes were added
class MockUserItem extends AutoDisposeNotifier<User>
with Mock
implements UserItem {}
注释掉接下来的两行
// notifier_provider_page_test.dart
// import 'notifier_provider_page_test.mocks.dart';
// @GenerateNiceMocks([MockSpec<UserItem>()])
此时会出现以下错误 mockito的官方文档有,但这并不能解决问题,因为build方法的返回值必须改变。
type 'Null' is not a subtype of type 'User'
有没有一种方法可以在不改变真实类的情况下进行测试?
Riverpod 的美妙之处在于您不需要模拟提供者/通知者。您可以轻松创建存根。
我不熟悉 Riverpod 代码生成的内部工作原理,因此这个示例可能不一对一匹配,可能需要一些调整。从阅读您的代码示例来看,它看起来是一个通知程序。
这里是如何在没有任何模拟库的情况下对通知程序进行存根以在测试中使用的示例。
假设我们有一个名为
UserItemNotifier
的通知程序,它从其 UserItem
方法返回 build
。要创建存根,我们可以在测试中创建一个名为 UserItemNotifierStub
的新类。构造函数中的_initialValue
允许您传入任意UserItem
,以便您可以测试多个场景。
class UserItemNotifierStub extends UserItemNotifier {
MyNotifierStub(this._initialValue);
final UserItem _initialValue;
@override
UserItem build() => _initialValue;
}
然后您可以在测试中使用此存根,如下所示:
late ProviderContainer container;
const user = const User(name: "a", email: "b", password: "c")
setUp(() {
container = ProviderContainer(overrides: [
userItemProvider.overrideWith(() => UserItemNotifierStub(user))
]);
});
我希望这有助于理解如何在测试中使用 Riverpod。