Flutter模拟数据插件mockor的使用
Flutter模拟数据插件mockor的使用
mockor
Generic mocker method generator for mockito 或 mocktail。
一个 mock
方法可以模拟任何类,就像在原始的 mockito 中一样。
开始使用
添加依赖
在 pubspec.yaml
文件中添加以下内容:
dev_dependencies:
mockor: ^1.0.0
创建 mocker.dart
文件并定义模拟方法
在测试文件夹中创建一个名为 mocker.dart
的文件,并添加一个带有 @GenerateMocker
注解的模拟方法。别忘了导入 mockito
。
// 这个导入用于生成的 mockor 文件以添加 Mockito 的 `@GenerateMocks` 注解。
// 需要手动添加。
import 'package:mockito/mockor.dart';
// <file_name>.mocks.dart 将由 Mockito 生成,其中包含所有生成的模拟。
// 需要手动添加。
import 'example.mocks.dart';
import 'package:mockor/mockor.dart';
part 'example.mockor.dart';
abstract class ExampleUseCase {
int exampleInt(int i);
}
abstract class ExampleUseCase2 {
int? exampleNullableInt();
void exampleVoid();
}
@GenerateMocker([
ExampleUseCase,
ExampleUseCase2,
])
T mock<T extends Object>({bool relaxed = false}) => _$mock<T>(relaxed: relaxed);
使用生成的模拟方法
在测试文件中导入并调用定义的模拟函数。
import '../../mocker.dart';
void main() {
late ExampleUseCase exampleUseCase;
late ExampleUseCase2 exampleUseCase2;
setUp(() {
// 这将返回 [MockExampleUseCase]
exampleUseCase = mock();
exampleUseCase2 = mock();
});
test("given exampleNullableInt throws an exception then don't catch it", () {
when(exampleUseCase2.exampleNullableInt()).thenThrow(Exception());
try {
exampleUseCase2.exampleNullableInt();
fail('expected exception');
} on Exception {}
});
test('given exampleInt with any param returns 2 then return 2', () {
/**
* 默认会为所有 [GenerateMocker.types] 生成一个 `asMock` 扩展方法,
* 它将其转换为生成的模拟类型(MockExampleUseCase)。
* 由于空安全,我们只能在使用模拟类型时对非空参数使用 `[any]` 匹配器。
* 请参阅 Mockito 的 [Null Safety README](https://github.com/dart-lang/mockito/blob/master/NULL_SAFETY_README.md) 获取更多信息。
*/
when(exampleUseCase.asMock().exampleInt(any)).thenReturn(2);
expect(exampleUseCase.exampleInt(1), 2);
});
test("given relaxed is true then return null on non null method not stubbed", () {
final ExampleUseCase2 useCaseRelaxed = mock(relaxed: true);
try {
useCaseRelaxed.exampleNullableInt();
} on MissingStubError {
fail("did not expect $MissingStubError");
}
});
// 注意:此测试仅在 `mockito` 中成功,而在 `mocktail` 中失败。
test("given relaxed is false then don't throw exception on void method not stubbed", () {
final ExampleUseCase2 useCase = mock(relaxed: false);
try {
useCase.exampleVoid();
} on MissingStubError {
fail("did not $MissingStubError");
}
});
}
使用 mocktail
修改 mocker.dart
文件
按照前面的步骤进行操作,但需要对 mocker.dart
文件进行一些修改。
// 这个导入用于生成模拟。
// 需要手动添加。
import 'package:mocktail/mocktail.dart';
import 'package:mockor/mockor.dart';
part 'mocker.mockor.dart';
@GenerateMocker.mocktail(
[
ExampleUseCase,
ExampleUseCase2,
],
// 可选参数:生成 `relaxedVoid` 参数以防止 `void` 和 `Future<void>` 缺失 stub 错误
generateRelaxedVoidParameter: true,
// 可选参数:生成非空参数的回退值以使用 `any()`
generateMocktailFallbackValues: GenerateMocktailFallbackValues(
[ExampleModel],
// 可选参数:自动检测所有方法的所有非基本参数的模拟类
autoDetect: true,
),
)
T mock<T extends Object>({bool relaxed = false, bool? relaxedVoid}) =>
_$_mock<T>(relaxed: relaxed, relaxedVoid: relaxedVoid);
// 定义一个全局注册回退值的方法
void registerFallbackValuesAll() {
_$registerFallbackValues();
}
创建 flutter_test_config.dart
文件
在测试文件夹的根目录下创建一个名为 flutter_test_config.dart
的文件。
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'mocker.dart';
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
// 这将在测试文件夹中的任何测试文件的主函数之前执行。
setUpAll(() {
registerFallbackValuesAll();
});
await testMain();
}
使用 relaxed
参数返回 null
_$mock<T>()
方法接受一个名为 relaxed
的可选布尔参数。
如果 false
,则会在 Mock
对象上调用 throwOnMissingStub
,这样当你尝试调用未 stub 的方法时会抛出异常。
但如果 true
,对于非空返回类型,会抛出 TypeError
。对于空返回类型,未 stub 的方法将返回 null。
默认情况下,mocktail
为 true
,而 mockito
为 false
。
T mock<T extends Object>({bool relaxed = false}) => _$mock<T>(relaxed: relaxed);
final myService = mock<MyService>(relaxed: true);
when(myService.doSomethingElse()).thenReturn(true);
myService.doSomething(); // 这将抛出异常
myService.doSomethingElse(); // 这不会抛出异常
与原生 mockito
相比的优势
始终可以使用基类型
- 重命名类不会破坏测试。
- 查找用法不会遗漏任何内容。
- 不必直接导入/依赖于模拟对象。
运行时的 relaxed
参数
生成的 _$mock
方法中添加了一个可选的 relaxed
参数。
当设置为 true
时,未 stub 的空方法返回 null
。对于未 stub 的非空方法,则抛出 TypeError
。
这在上面已有详细解释。
与原生 mocktail
相比的优势
快速代码生成
尽管 mocktail
是为了避免代码生成而创建的,但在这种情况下仍需要代码生成,但仅限于包含通用模拟函数的一个文件。
你的任何测试都不需要依赖生成的代码。每个模拟类只有 1 行代码。
私有模拟类
生成的模拟类是私有的,因此不能被导入。只需调用全局 mock
方法。
这使得所有开发人员更容易遵循一致的编码约定。
生成 registerFallbackValues
函数
mocktail
的一个令人烦恼的缺点是你需要注册回退值才能调用它们上的 any()
函数。
使用 mockor
,你可以生成一个 registerFallbackValues
函数,你可以在其中手动指定类型或让生成器自动检测它们。
有关更多信息,请参阅上面的开始部分。
可选的 relaxed
参数
生成的 _$mock
方法中添加了一个可选的 relaxed
参数。
当设置为 true
时,未 stub 的空方法返回 null
。对于未 stub 的非空方法,则抛出 TypeError
。
这在上面已有详细解释。
可选的 relaxedVoid
参数(仅限 mocktail
特性)
生成的 _$mock
方法中添加了一个可选的 relaxedVoid
参数。
当设置为 true
时,未 stub 的 void
和 Future<void>
方法不会抛出错误。
常见问题解答
如何隐藏生成的模拟类?
在项目的根目录下添加一个 analysis_options.yaml
文件,内容如下:
analyzer:
exclude:
- '**/*.mocks.dart' # Mockito @GenerateMocks
故障排除
错误:“未生成模拟类 ‘MockExampleUseCase’”
模拟方法仅支持返回其基类型的模拟实例。
错误:使用 any
时,“参数类型 ‘Null’ 无法分配给参数类型…”
为了能够使用 any
,你需要的是被模拟的类型而不是基类型。
为此生成了一个 asMock
扩展方法。
extension ExampleUseCaseAsMockExtension on ExampleUseCase {
MockExampleUseCase asMock() => this as MockExampleUseCase;
}
然后在测试中使用:
abstract class ExampleUseCase {
int example(int i);
}
final ExampleUseCase useCase = mock();
// 因为 int i 是非空的,`any` 返回 null。所以必须使用 `asMock`,因为在 MockExampleUseCase 中该方法被重写为允许空参数。
when(useCase.asMock().example(any)).thenReturn(1);
更多关于Flutter模拟数据插件mockor的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter模拟数据插件mockor的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
mockor
是一个用于 Flutter 的模拟数据生成插件,它可以帮助开发者在开发和测试阶段生成模拟数据,从而避免依赖于真实的后端服务。通过 mockor
,你可以轻松地创建和注入模拟数据,以便在开发过程中进行测试和调试。
以下是使用 mockor
插件的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 mockor
插件的依赖。
dependencies:
flutter:
sdk: flutter
mockor: ^1.0.0 # 请根据需要选择最新版本
然后运行 flutter pub get
来获取依赖。
2. 创建模拟数据类
接下来,你可以创建一个模拟数据类,并使用 @GenerateMocks
注解来标记它。mockor
会根据这个注解生成相应的模拟数据代码。
import 'package:mockor/mockor.dart';
@GenerateMocks([User])
class User {
final String name;
final int age;
User(this.name, this.age);
}
3. 生成模拟数据代码
运行 flutter pub run build_runner build
来生成模拟数据代码。这将会在你的项目目录下生成一个 .mockor
文件,其中包含了模拟数据类的实现。
4. 使用模拟数据
生成代码后,你可以使用生成的模拟数据类来创建模拟对象。
import 'package:your_project/mockor.dart';
void main() {
final mockUser = MockUser();
mockUser.name = 'John Doe';
mockUser.age = 30;
print('Mock User: ${mockUser.name}, ${mockUser.age}');
}
5. 注入模拟数据
在开发过程中,你可以将模拟数据注入到你的应用中进行测试。例如,在依赖注入框架中,你可以将模拟数据类替换为真实的服务类。
import 'package:your_project/mockor.dart';
class UserService {
final User user;
UserService(this.user);
void printUserInfo() {
print('User: ${user.name}, ${user.age}');
}
}
void main() {
final mockUser = MockUser();
mockUser.name = 'Jane Doe';
mockUser.age = 25;
final userService = UserService(mockUser);
userService.printUserInfo();
}
6. 测试
在编写测试时,你可以使用 mockor
生成的模拟数据类来替代真实的依赖,从而简化测试流程。
import 'package:flutter_test/flutter_test.dart';
import 'package:your_project/mockor.dart';
void main() {
test('UserService should print user info', () {
final mockUser = MockUser();
mockUser.name = 'Test User';
mockUser.age = 99;
final userService = UserService(mockUser);
userService.printUserInfo();
expect(mockUser.name, 'Test User');
expect(mockUser.age, 99);
});
}