Step CI および stepci/runner を利用した、WebAPI実行ツールを作成しました。
特徴として、ワークフロー単位での、チェイニングすることも可能です。
WebAPI 用テストツールとして Step CI を利用します。
以下を参照してください。
tests ディレクトリ内にテストコードを準備します。
コード記述については以下ドキュメントを参照してください。
(全て英語ですが ブラウザの翻訳機能を使うと、要素がきちんと作られたページなので、ストレスなしで読めます。)
OpenAPI Scheram にも対応しています。
https://swagger.io/specification/
以下のコマンドにて、StepCIをインストールしてください。
npm install --save-dev stepciSTEP CI では Captureを利用することで、前のステップで得た結果を次のステップへ利用することが可能です。 ですかこれには限界があります。
例えば captureしたpresignedURLを利用して、フロントエンドからS3へアップロードする動作自体を再現することができません。
Shellにて、各yamlファイルを実行しても、この結果をShell側に引き渡すことができないのです。
これを解決するため、stepci/runner を導入しました。
TypeScriptで実行結果をPromise<WorkflowResult>型で取得できるので、そこから、起動側へ受け渡すこと可能になります。
また、stepci/runnerを導入してますので以下もインストールしてください。
npm install --save-dev @stepci/runnernode.js上で、TypeScriptを実行するには、一旦 tsc コマンドでjavascriptにコンパイル(コンバート)する必要があり、その生成したJavaSrciptを実行する必要があります。
ワンステップで実行を行いたい為、今回はts-nodeを導入します。
また、Node.jsに必要な型定義ファイルである@types/nodeも念の為導入します。
npm install --seve-dev @types/node
npm install --save-dev ts-nodeTip
すでにpackage.jsonに組み込み済みなので、sign-api をclone時には以下のコマンドでインストールされます。
npm installテスト環境などの共通環境変数をtest.env.tsで定義します。
const HOST = "localhost:5000";定数とし定義した環境変数は、commonEnvVarオブジェクトにプロパティとして追加します。
// 共通環境変数オブジェクト
export const commonEnvVar = {
host: HOST, // 👈 追加した変数
fontendHost: FRONTEND_HOST,
baseurl: BASEURL,
today: TODAY,
now: NOW,
expiredday: EXPIRED_DAY,
basedir: BASE_DIR,
testWorkllowsDir: TEST_WORKFLOWS_DIR,
testResourcesDir: TEST_RESOURCES_DIR,
STEPCI_DISABLE_ANALYTICS: "1"
};これらは、TestExecutorがimportし利用しており、各テストワークフローへenvとしてそれぞれのプロパティが追加され、テストワークフロー(YAMLファイル定義)へ引き渡されます。。
Object.assign(testYaml.options.env, commonEnvVar);テストワークフローであるYAMLファリルでは、以下のように定義した変数を利用します。
host: ${{env.host}}以下を参考に、テストシナリオをYAMLにてワークフロー単位で記載し追加してください。
[!NOTE] Step CIには、OpenAPIに似た
$ref構文をして、ワークフローにtestまたはstep単位でインポートが可能です。ただし柔軟に利用するには少し不便です。 https://docs.stepci.com/guides/organising-workflows.html
worklows.tsのtestYamlsリストの部分の下に、必要であればパラメータやリポートクラスを指定し、追加してください。
export const testYamls: TestYaml[] = [
// { fileName: 'auth.yml' },
// {
// fileName: 'mail.getSentMail.yml',
// wait: 15000,
// options: { env: { toUserAccount: 'user1@example.jp' } },
// workflowDataHandler: getLinkInUserRegistEmail
// },
// { fileName: 'user.registration.yml', reporter: customResultReporterSample },
// ここに新たなテストコードを追加してください。mail.getRegistrationMail
{ fileName: 'auth.yml' },
];単体で実行する場合など、他をコメントアウトするなど工夫をしてください。
TestYaml型の説明は以下通りです。
| プロパティ | 型 | 説明 |
|---|---|---|
| fileName | string | テストワークフローファイル名 |
| wait? | number | ワークフロー実行するまでの待機時間 |
| options? | WorkflowOptions | 実施するテストワークフロー固有の WorkflowOptionsオプションがあるならここで指定する。 |
| workflowDataHandler? | Function | オプションとして、テストワークフロー後の処理を記述します。結果から取得した値を元にAPI以外の処理を行い、その結果を次のテストワークフロー引き渡したりするなどの処理をここで行う。 |
| reporter? | Function | オプションとして、カスタム表示を定義した関数を指定 |
コードの準備ができ、ローカル開発環境の各コンテナ(docker-compose up)起動後に、以下のように npm run コマンドにて、StepCI を実行します。
npm run test:apiまた、tests毎の詳細表示など行いたい場合は、以下を実行してください。
npm run test:api-vデバックなどには、以下のようにより詳細にリクエスト・レスポンスを確認できます。
npm run test:api-t > hoge.txtテストに成功した場合は、各ワークフローごとにPASSEDと表示します。
今後、表示結果などの整形する必要はあるかと思いますが、こちらは別途対応となります。
% npm run test:api
> @nasori-llc/sign-api@0.0.1 test:api
> npx ts-node ./WebAPITest/test.runner.ts
||||||||| Web API Test Start: 2024/10/24 8:09:52 ||||||||||
.../sign/sign-api/WebAPITest/tests/auth.yml Result: PASSED
.../sign/sign-api/WebAPITest/tests/user.signup.yml Result: PASSED
.../sign/sign-api/WebAPITest/tests/user.registration.yml Result: PASSED
.
.
.
||||||||| Web API Test End: 2024/10/24 8:13:32 ||||||||||エラーの場合は、以下のようにFAILEDと表示され、エラー内容がStep毎に表示されます。
.../sign/sign-api/WebAPITest/tests/account.delete.yml Result: FAILED
- Step 3 - Test name: Get Delete Account Notice Email, Error Message: undefinedstepci/runner は Promise<WorkflowResult>型で結果が返ってきるので、この中のresultオブジェクトを出力してレポーティングしてます。
エラーの際は以下のコマンドにて詳細を表示し、 passed: false,などを検索し確認してください。
npm run test:api-t > hoge.log
open hoge.logpackage.jsonに登録した、npm スクリプトは以下となります。
"test:api": "npx ts-node ./test.runner.ts",
"test:api-v": "npx ts-node ./test.runner.ts -v",
"test:api-t": "npx ts-node ./test.runner.ts --trace"
| ファイル名 | 説明 |
|---|---|
| tests/*.yml | テストワークフローYAMLファイル 例: auth.yml |
| tests/..yml | $ref 参照によって呼び出される yamlファイル例: auth.userAuthentication.yml |
| test.env.ts | テスト実行時の環境変数を格納したファイル |
| workflow.ts | テスト実行するコアファイル。npx npm run にて呼び出される。テストワークフローYAMLファイルのリスト情報 testYamlsを保持。このファイルのリストに定義したテストコードを追加する。 |
| test.executor.ts | テスト実行クラス |
| test.reporter.ts | テスト結果をリポート表示する関数群 |
| test.result-handler.ts | テスト実行後にテスト結果などを利用した、テスト以外の後処理など実行させるための関数群 |
| utils.ts | コマンドオプションの判定ユーティリティ |
| tsconfig.json | typescript設定ファイル |
前のテストワークフロー結果から得た値を、次のテストワークフローに引き渡して実行する方法を想定して、test.result-handler.ts、テスト結果をハンドリングする関数を追加しました。
このような関数をテストワークフロー一覧(testYamls)に指定することで、単にワークフローチェインイングを目的とした、そのままチャプチャした値をenvとして返却するシンプルな使い方もあれば、テストワークフローの結果から得た値で、ストレージや他のAPIにアクセスし、その結果を返し次のワークフローに引き渡すような処理の記述も可能になります。。
export declare type WorkflowOptions = {
path?: string;
secrets?: WorkflowOptionsSecrets;
ee?: EventEmitter;
env?: WorkflowEnv;
concurrency?: number;
};- getLinkInUserRegistEmail: 登録確認用メールのLinkよりパラメーターを取得する
- getLinkInApproverRequestEmail: 承認依頼メールのLinkよりパラメーターを取得する
/** 登録確認用メールのLinkよりパラメーターを取得する */
export const getLinkInUserRegistEmail = (workflowResult: WorkflowResult): WorkflowOptions | null => {
const regexPattern = `<a href="(http:\\/\\/${commonEnvVar.frontendHost}\\/register[^"]*)"`;
const prefix = 'registration_user_'
const url = getLinkUrlFromMail(workflowResult, regexPattern);
const env = getLinkParamsData(url, prefix);
if (Object.keys(env).length === 0) return null;
return { env };
}
/** 承認依頼メールのLinkよりパラメーターを取得する */
export const getLinkInApproverRequestEmail = (workflowResult: WorkflowResult): WorkflowOptions | null => {
const regexPattern = `<a href="(http:\\/\\/${commonEnvVar.frontendHost}\\/documents\\/([0-9a-f-]+)\\/approve\\?token=([^"]+))"`;
const prefix = 'approve_document_'
const pathParamsName = 'id';
const pathRegexPattern = `([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})`;
const url = getLinkUrlFromMail(workflowResult, regexPattern);
const params = getLinkParamsData(url, prefix);
const pathParam = getLinkPathParamsData(url, pathRegexPattern, pathParamsName, prefix)
const env = { ...params, ...pathParam };
if (Object.keys(env).length === 0) return null;
return { env };
}options = {hoge: 'hoge'};
export const testYamls: TestYaml[] = [
{ fileName: "auth.yml", workflowDataHandler: getLinkInUserRegistEmail },
];以下のインターフェイスを実装し、テストシナリオ(テストワークフロー)リストのtestYamlsにて、workflowDataHandler?に、そのクラスを指定すれば、そのテスト結果を基に、テスト以外の処理を行なったり、CapturesStorage型で結果を返し、この結果を次のテストワークフローに引き渡して、チェーンニングを実現できます。
export interface IWorkflowDataHandler {
execute(workflowResult: WorkflowResult): WorkflowOptions | null;
}export declare type WorkflowOptions = {
path?: string;
secrets?: WorkflowOptionsSecrets;
ee?: EventEmitter;
env?: WorkflowEnv;
concurrency?: number;
};export class MailHogHook implements IWorkflowDataHandler{
execute(workflowResult: WorkflowResult): WorkflowOptions | null {
throw new Error("Method not implemented.");
// MailHog APIを利用し、PostParamsにて指定したメールをフックし、指定した値をキャッチし返却する。
}
}options = {hoge: 'hoge'};
export const testYamls: TestYaml[] = [
{ fileName: "auth.yml", workflowDataHandler: new GetLinkInEmail() },
];デフォルトでは、test.reporter.tsに定義した、resultReporter関数が実行されます。
test.reporter.tsに、同じようにリポーター関数を定義し、testYamlにて、そのワークフローで指定することによって、ワークフロー固有の表示形式に変更することができます。
以下は、customResultReporterSample関数をtest.reporter.tsに実装し、worklows.tsのtestYamlsに追加した例です。
options = {hoge: 'hoge'};
export const testYamls: TestYaml[] = [
{ fileName: "hoge.yml", reporter: customResultReporterSample },
];実装例)ファイル出力、や結果のみ整形して通知など
上記確認すると、runFromFile(path: string, options?: WorkflowOptions)の WorkflowOptionsにより、環境変数以外にシークレット情報や、イベントエミッターなどを設定できます。
ReadMe.mdでも、記載しているとおりWorkflowOptionsのeeオプションに、EventEmitterを指定すれば、各イベントのフックやダミーイベントを作成することも可能のようです。
| イベント名 | 説明 |
|---|---|
| step:http_request | HTTPリクエストが行われたときのイベント |
| step:http_response | HTTPレスポンスを受け取ったときのイベント |
| step:grpc_request | gRPCリクエストが行われたときのイベント |
| step:grpc_response | gRPCレスポンスを受け取ったときのイベント |
| step:result | ステップが完了したときのイベント |
| step:error | エラーが発生したときのイベント |
| test:result | テストが完了したときのイベント |
| workflow:result | ワークフローが完了したときのイベント |
| loadtest:result | ロードテストが完了したときのイベント |
以下のコードでは、HTTPリクエストが行われたときにリクエスト内容を表示するよう指定したサンプルです。
....
// EventEmitterのインスタンスを作成
const emitter = new EventEmitter();
// HTTPリクエストが行われたときのイベント
emitter.on('step:http_request', (request) => {
console.log('HTTP Request Made:', request);
});
export const testYamls: TestYaml[] = [
{ fileName: "hoge.yml", options: { ee: emitter } },
// ここに新たなテストコードを追加してください。
];
....リクエスト時の内容を表示
....
},
emit: [Function (anonymous)],
[Symbol(shapeMode)]: false,
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
'user-agent': [ 'user-agent', 'got (https://github.com/sindresorhus/got)' ],
accept: [ 'accept', 'application/json' ],
'content-type': [ 'content-type', 'application/json' ],
authorization: [
'authorization',
'Bearer ....'
],
'content-length': [ 'content-length', '1496' ],
'accept-encoding': [ 'accept-encoding', 'gzip, deflate, br' ],
host: [ 'Host', 'localhost:5000' ]
},
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null,
[Symbol(reentry)]: true
}
....
emitter.emit('step:http_response', { statusCode: 200, body: 'OK' });