Capacitor プラグインをモックする
アプリケーション内でユニットテストを作成する場合、 テスト対象のユニットに対する外部依存関係のモックを作成するのがベストプラクティスです。これには、コンポーネントやサービスが使用している Capacitor プラグインも含まれます。
ほとんどのモッキングライブラリは、オブジェクトを取得して JavaScript プロキシでラップすることでモックを作成し、そのオブジェクト上のメソッドの呼び出しを調べたりメソッドの戻り値を制御したりします。しかし、CapacitorプラグインはJavaScriptレイヤーの中でプロキシとして実装されています。プロキシのプロキシを作成することはサポートされておらず、失敗します。この問題を回避するために、手動モックを使用することができます。
手動モック
手動モックを使用すると、JavaScript モジュール全体の機能を簡単にスタブ化することができます。その結果、テストが import { Storage } from '@capacitor/storage' を実行すると、本当の Storage JavaScript proxy object をロードする代わりに、以下のようなものをロードすることになります。
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オブジェクトなので、スパイするのは非常に簡単です。また、モックであるため、ネイティブ呼び出しを試みません。これにより、Capacitorプラグインを使用するコードをテストする際に、手動モックの使用が理想的な選択となります。
Jest
Jestテストフレームワークには手動モックが組み込まれています。プロジェクトのルートに__mocks__/@capacitorフォルダを作成すると、Jestはnode_modulesからではなくそこからファイルを自動的に読み込みます。
たとえば、次のディレクトリ構造があるとします:
.
|
+- __mocks__
| |
| +- @capacitor
| |
| +- storage.ts
| +- toast.ts
...
+- src
テストでは、node_modulesの実際の@capacitor/storageおよび@capacitor/toastプラグインではなく、storage.tsとtoast.tsで定義されたスタブが使用されます。
Jasmine
Jasmineテストフレームワークには「手動モック」の概念は含まれていませんが、TypeScriptのパスマッピングを使用することで簡単にシミュレートできます。
まず、Jestの例と同様に、プロジェクトのルートレベルに同じディレクトリ構造を作成します。
Angularプロジェクト(テストフレームワークとしてJasmineを使用する最も一般的なシナリオ)には、ユニットテストが実行されるときにtsconfig.jsonベース設定を拡張するtsconfig.spec.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/*"]
}
これで、ユニットテストがコンパイルされるとき、import { Storage } from '@capacitor/storage';はnode_modulesの実際のものではなく、__mocks__/@capacitorの下のスタブファイルを使用します。
注意: pathsオブジェクトはマージされるのではなく完全に置き換えられるため、tsconfig.jsonで定義されているパスがある場合は、tsconfig.spec.jsonにも含める_必要があります_。
スタブのモック
手動モックが配置されたら、通常の方法でメソッド呼び出しをモックおよびスパイするテストを作成できます。
Jest
it("gets the first and last name", async () => {
Storage.get = jest.fn().mockImplementation(
async (data: { key: string }): Promise<{ value: string }> => {
return data.key === "firstName"
? { value: "Jimmy" }
: data.key === "lastName"
? { value: "Simms" }
: { value: "unknown" };
}
);
const w = mount(Home);
await flushPromises();
expect(w.vm.firstName).toEqual("Jimmy");
expect(w.vm.lastName).toEqual("Simms");
});
it("clears the storage", () => {
const button = wrapper.findComponent('[data-testid="clear"]');
Storage.clear = jest.fn().mockResolvedValue(undefined);
button.trigger("click");
expect(Storage.clear).toHaveBeenCalledTimes(1);
});
Jasmine
it("gets the first and last name", async () => {
spyOn(Storage, 'get');
(Storage.get as any)
.withArgs({ key: 'firstName' })
.and.returnValue(Promise.resolve({ value: 'Jason' }));
(Storage.get as any)
.withArgs({ key: 'lastName' })
.and.returnValue(Promise.resolve({ value: 'Jones' }));
fixture.detectChanges();
await fixture.whenRenderingDone();
expect(component.firstName).toEqual('Jason');
expect(component.lastName).toEqual('Jones');
});
it('clears the storage', () => {
spyOn(Storage, 'clear');
click(clear.nativeElement);
fixture.detectChanges();
expect(Storage.clear).toHaveBeenCalledTimes(1);
});