模拟 Capacitor 插件
在应用程序中编写单元测试时,最佳实践是为被测单元的任何外部依赖创建模拟(mock)。这包括你的组件或服务正在使用的 Capacitor 插件。
大多数模拟库通过获取一个对象并将其包装在 JavaScript 代理(Proxy)中来创建模拟,从而可以检查对该对象方法的调用并控制方法的返回值。然而,Capacitor 插件在 JavaScript 层本身就是作为代理实现的。为代理创建代理是不被支持且会失败的。手动模拟(Manual Mock)可以用来规避此问题。
手动模拟
手动模拟允许用户轻松地桩化(stub)整个 JavaScript 模块的功能。因此,当测试执行 import { Storage } from '@capacitor/storage' 时,不会加载真实的 Storage JavaScript 代理对象,而是加载如下所示的内容:
export const Storage = {
async get(data: { key: string }): Promise<{ value: string | undefined }> {
return { value: undefined };
},
async set(data: { key: string; value: string }): Promise<void> {},
async clear(): Promise<void> {},
};
由于这是一个普通的 JavaScript 对象而不是代理对象,因此很容易对其进行监视(spy)。同时,由于它是模拟对象,不会尝试进行任何原生调用。这使得使用手动模拟成为测试使用 Capacitor 插件的代码时的理想选择。
Jest
Jest 测试框架内置了手动模拟功能。在项目根目录下创建一个 __mocks__/@capacitor 文件夹,Jest 将自动从该目录加载文件,而不是从 node_modules 加载。
例如,假设你有以下目录结构:
.
|
+- __mocks__
| |
| +- @capacitor
| |
| +- storage.ts
| +- toast.ts
...
+- src
你的测试将使用 storage.ts 和 toast.ts 中定义的桩代码,而不是来自 node_modules 中真实的 @capacitor/storage 和 @capacitor/toast 插件。
Jasmine
Jasmine 测试框架本身没有"手动模拟"的概念,但我们可以通过使用 TypeScript 路径映射(path mapping)来轻松模拟这一功能。
首先,在项目根目录下创建与 Jest 示例相同的目录结构。
Angular 项目(使用 Jasmine 作为测试框架的最常见场景)包含一个 tsconfig.spec.json 文件,该文件在执行单元测试 时会继承 tsconfig.json 的基础配置。修改此文件以扩展你在基础配置中可能已有的 paths 映射。
例如,如果你的 tsconfig.json 文件包含以下 paths 映射:
"paths": {
"@app/*": ["src/app/*"],
"@env/*": ["src/environments/*"]
},
然后更新你的 tsconfig.spec.json 文件,使其包含这些路径以及你想为单元测试使用的路径:
"paths": {
"@app/*": ["src/app/*"],
"@env/*": ["src/environments/*"],
"@test/*": ["test/*"],
"@capacitor/*": ["__mocks__/@capacitor/*"]
}