注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
@osdk/integration-testing パッケージを使用すると、ローカルのオントロジーサーバーに対して TypeScript オントロジー SDK (OSDK) コードをテストできます。オントロジーのメタデータとテストデータを指定し、OSDK の Client を使用してコードを実行します。ローカルサーバーは、そのデータに対してオブジェクトクエリを評価します。
フィルター、オブジェクトの読み取り、リンク、集計がどのように連携するかを確認するには、統合テストを使用します。スタブでクライアントの各呼び出しの結果を指定したい場合は、ユニットテストを使用します。
このライブラリは、主に次の3つの要素を提供します。
IntegrationServer: Foundry CLI を介してローカルのオントロジーサーバーを起動および停止します。SeedClient: そのサーバー上でテストデータを作成および変更します。IntegrationClient: ローカルサーバーに OSDK リクエストを送信し、関数クエリのスタブをサポートします。@osdk/integration-testing をインストールして環境を準備する統合テストは Node.js 環境で実行してください。このライブラリはローカルプロセスを起動するため、ブラウザーテスト向けではありません。ライブラリと @osdk/seed-helpers を開発用の依存関係としてインストールします。
Copied!1npm install --save-dev @osdk/integration-testing @osdk/seed-helpers
プロジェクトには、互換性のあるバージョンのピア依存関係 @osdk/api と @osdk/client、および生成済みの TypeScript SDK も必要です。
このページの例では、Vitest ↗ を使用します。同等のセットアップフックとクリーンアップフックを備えた、別の Node.js テストランナーも使用できます。Vitest を使用するには、開発用の依存関係としてインストールします。
Copied!1npm install --save-dev vitest
以下の例では、SDK に Employee オブジェクトタイプがあり、employeeId という名前の文字列型の主キーと、office という名前の文字列型のプロパティがあることを前提としています。
OSDK の Client を受け取り、オントロジーリソースを読み取る関数をテストしたいとします。たとえば、src/countEmployeesInOffice.ts は、指定したオフィスの従業員数を数えます。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13import type { Client } from "@osdk/client"; import { Employee } from "your-app-sdk"; export async function countEmployeesInOffice( client: Client, officeId: string, ): Promise<number> { const result = await client(Employee) .where({ office: { $eq: officeId } }) .aggregate({ $select: { $count: "unordered" } }); return result.$count; }
次のテストファイル test/countEmployeesInOffice.integration.test.ts は、統合クライアントを関数に渡し、その結果をシードデータと照合します。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42import { createIntegrationServer, type IntegrationClient, type IntegrationServer, type SeedClient, } from "@osdk/integration-testing"; import { afterAll, beforeAll, beforeEach, expect, it } from "vitest"; import { Employee } from "your-app-sdk"; import metadata from "your-app-sdk/experimental/ontology-metadata" with { type: "json", }; import { countEmployeesInOffice } from "../src/countEmployeesInOffice.js"; let server: IntegrationServer | undefined; let client: IntegrationClient; let seed: SeedClient; beforeAll(async () => { server = await createIntegrationServer({ metadata }); await server.start(); client = await server.getClient(); seed = await server.getSeedClient(); }, 180_000); beforeEach(async () => { client.clearStubs(); await seed.set(); }, 30_000); afterAll(async () => { await server?.stop(); }, 30_000); it("counts only employees in the requested office", async () => { await seed((s) => { s.create(Employee, { employeeId: "employee-1", office: "north" }); s.create(Employee, { employeeId: "employee-2", office: "north" }); s.create(Employee, { employeeId: "employee-3", office: "south" }); }); expect(await countEmployeesInOffice(client, "north")).toBe(2); }, 30_000);
テストを実行します。
Copied!1npx vitest run test/countEmployeesInOffice.integration.test.ts
このテストはサーバーを起動し、3個のオブジェクトをシードデータとして登録して、統合クライアントを関数に渡します。サーバーはフィルターと集計を評価します。どちらの操作にもスタブを登録する必要はありません。フックのタイムアウトを長くすることで、ローカルサーバーの起動時間を確保し、テストの終了後に afterAll がサーバーを停止します。
シードとは、ローカルサーバーに読み込むオブジェクトとリンクをまとめたものです。SeedRef は、そのシード内のオブジェクトを識別します。シード参照を使用してオブジェクトを更新したりリンクを作成したりし、コードで OSDK インスタンスが必要な場合は、統合クライアントを通じてオブジェクトを取得します。
非同期のシード操作を行うたびに、それまでに蓄積されたシードがサーバーに適用されます。データを読み取る前に、その操作の完了を待ってください。seed.ref() は同期的で、現在のシードを読み取るだけです。
| メソッド | 用途 |
|---|---|
await seed.create(Type, properties) | オブジェクトを作成し、その参照を返します。主キーと必須プロパティを含めてください。同じタイプで主キーが重複すると、エラーが発生します。 |
seed.ref(Type, primaryKey) | シードに登録されたオブジェクトへの参照を返します。存在しない場合は undefined を返します。 |
await seed.update(ref, properties) | 主キーを変更せずに、オブジェクトにプロパティをマージします。省略したプロパティは変更されません。オブジェクトが存在しない場合は、作成します。 |
await seed.delete(ref) | シードからオブジェクトを削除します。オブジェクトが存在しない場合は、何もしません。 |
await seed.link(source, linkApiName, target) | 対象の参照、または対象の参照の配列への多対多リンクを追加します。 |
await seed.unlink(source, linkApiName, target) | 指定したシード内のリンクを削除します。 |
await seed.set(output) | シード全体を SeedOutput で置き換えます。クリアするには、await seed.set() を使用します。 |
await seed.addAll(output) | 現在のシードに SeedOutput を追加します。オブジェクトの主キーは、既存の主キーと重複してはいけません。 |
await seed(callback) | 同期的な SeedBuilder コールバックを呼び出し、シードを1回適用して、コールバックの結果を返します。 |
await seed(output) | SeedOutput を追加します。動作は seed.addAll(output) と同じです。 |
たとえば、従業員を作成し、そのオフィスを変更して、更新されたオブジェクトを読み取り、削除します。
Copied!1 2 3 4 5 6 7 8 9 10 11const employeeRef = await seed.create(Employee, { employeeId: "employee-1", office: "north", }); await seed.update(employeeRef, { office: "south" }); const employee = await client(Employee).fetchOne(employeeRef.$primaryKey); expect(employee.office).toBe("south"); await seed.delete(employeeRef);
コールバック形式を使用すると、複数の変更を行い、1回のリクエストで適用できます。コールバック内のビルダーメソッドは同期的です。コールバックを async にしないでください。
この例ではさらに、officeId という名前の文字列型の主キーを持つ Office オブジェクトタイプと、visitedOffices という名前の Employee の多対多リンクがあることを前提としています。
Copied!1 2 3 4 5 6 7 8 9 10 11 12import { Employee, Office } from "your-app-sdk"; const refs = await seed((s) => { const office = s.create(Office, { officeId: "north" }); const employee = s.create(Employee, { employeeId: "employee-1" }); s.link(employee, "visitedOffices", [office]); return { employee, office }; }); const employee = await client(Employee).fetchOne(refs.employee.$primaryKey); const offices = await employee.$link.visitedOffices.fetchPage(); expect(offices.data.map((office) => office.$primaryKey)).toEqual(["north"]);
現在、ローカルサーバーでは、seed.link() を通じて指定するリンクの両端に文字列型の主キーが必要です。外部キーに基づいているリンクの場合は、オブジェクトの外部キープロパティをシードデータとして登録してください。
@osdk/seed-helpers の createSeedWithMetadata を使用すると、サーバーに送信せずにフィクスチャを作成できます。この関数は、シードデータを保持する output と、コールバックの戻り値を保持する context を返します。
Copied!1 2 3 4 5 6 7 8 9 10 11import { createSeedWithMetadata } from "@osdk/seed-helpers"; const fixture = createSeedWithMetadata(metadata, (s) => { const employee = s.create(Employee, { employeeId: "employee-1", office: "north" }); return { employee }; }); beforeEach(async () => { client.clearStubs(); await seed.set(fixture.output); }, 30_000);
最初の例の空のシードフックの代わりに、このフックを使用します。各テストは同じデータで開始します。たとえば、fixture.context.employee.$primaryKey から、アサーションで使用する従業員の主キーを取得できます。
ローカルのオントロジーサーバーは、関数クエリを実行しません。whenQuery を使用して、期待される結果を統合クライアントに登録してください。オブジェクトの読み取りは、引き続きローカルサーバーに送られます。
数値型の n パラメーターを持つ、addOne という名前の生成済みクエリには、次を使用します。
Copied!1 2 3 4 5 6 7 8 9import { addOne } from "your-app-sdk"; client.whenQuery(addOne, { n: 5 }).thenReturn(6); client.whenQuery(addOne, { n: 99 }).thenThrow(new Error("Query failed")); expect(await client(addOne).executeFunction({ n: 5 })).toBe(6); await expect(client(addOne).executeFunction({ n: 99 })).rejects.toThrow( "Query failed", );
コードが渡すクエリ定義とパラメーターを使用してください。client.clearStubs() は登録されたクエリスタブを削除しますが、サーバーのシードデータはクリアしません。
テスト間でサーバーを共有する場合は、各テストの前に seed.set() または seed.set(fixture.output) でデータをリセットしてください。クエリスタブは、別途 client.clearStubs() でクリアします。
サーバーとシードクライアントを共有するテストは、順番に実行してください。並行してテストを実行する場合は、独立したテストコンテキストごとに個別のサーバーを作成します。各サーバーには専用の実行ディレクトリがありますが、同じサーバーを使用するテストはそのデータを共有します。
セットアップやアサーションに失敗した場合も含め、クリーンアップフックまたは finally ブロックで必ず server.stop() を呼び出してください。ローカルの統合テストは、ローカルのオントロジーサーバーがサポートする動作を確認しますが、Foundry での権限、デプロイ済みの関数、外部サービスのテストを代替するものではありません。