> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> **Actions NPM (`@auth0/actions`)** パッケージを使用すると、外部プロジェクトで TypeScript による開発が可能になり、開発者はベストプラクティスに従って、TypeScript の定義に基づく **ユニットテスト** を改善できます。

# Actions のユニットテスト

<div id="auth0-actions-unit-test">
  # Auth0 Actions Unit Test
</div>

**Actions NPM (`@auth0/actions`)** パッケージを使用すると、外部プロジェクトで TypeScript を用いた開発が可能になり、開発者はベストプラクティスに従って、TypeScript の型定義に基づくユニットテストを強化できます。

***

<div id="how-it-works">
  ## 仕組み
</div>

インストールとインポートのガイドラインについては、[How Actions NPM works](/ja/docs/customize/actions/actions-npm#how-it-works) を参照してください。

Action をユニットテストするには、Action 関数が受け取る `event` オブジェクトと `api` オブジェクトをモックする必要があります。これらのモックは、`auth0/actions` に含まれる TypeScript の定義を使用して作成できるため、テストで本番環境を正確に反映できます。モックや機能の検証を管理するには、Jest のようなテストフレームワークが適しています。

ユニットテストは、ローカル環境、バージョン管理、または CI/CD プロセスで実行できます。これにより、Auth0 テナントに変更を反映する前に、全体的な品質保証と検証を強化できます。

***

<div id="examples">
  ## 例
</div>

以下の例では、必要なオブジェクトをモックして、一連のシナリオを検証します。

<Note>
  これらの例では **Jest** (`https://www.npmjs.com/package/jest`) を使用していますが、任意のテストライブラリを使用できます。
</Note>

<div id="configuration">
  ### 設定
</div>

`package.json` で開発依存関係を定義すると、Action の作成時に IntelliSense の補完支援を利用できます。

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    {
      "name": "actions-js",
      "version": "1.0.0",
      "description": "Actions JS",
      "main": "example.js",
      "scripts": {
        "test": "jest"
      },
      "author": "John Doe",
      "license": "ISC",
      "devDependencies": {
        "@auth0/actions": "^0.7.1",
        "jest": "^29.7.0"
      }
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    {
      "name": "actions-ts",
      "version": "1.0.0",
      "description": "Actions TS",
      "main": "example.ts",
      "scripts": {
        "test": "jest"
      },
      "author": "John Doe",
      "license": "ISC",
      "devDependencies": {
        "@auth0/actions": "^0.7.1",
        "@types/jest": "^29.5.12",
        "@types/node": "22.14.0",
        "jest": "^29.7.0",
        "ts-jest": "^29.1.2",
        "typescript": "^5.9.2"
      }
    }
    ```

    `tsconfig.json` ファイルでは、TypeScript コンパイラの動作を定義します。

    ```typescript theme={null}
    {
      "compilerOptions": {
        "target": "ES2020",
        "module": "NodeNext",
        "moduleResolution": "nodenext",
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "outDir": "dist",
        "declaration": true,
        "sourceMap": true,
        "allowJs": true,
        "checkJs": false,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "isolatedModules": true
      },
      "include": [
        "**/*.ts"
      ],
      "exclude": [
        "node_modules",
        "dist"
      ]
    }
    ```

    `jest.config.js` では、`Jest` の環境プリセットを定義します。

    ```typescript theme={null}
    module.exports = {
      preset: 'ts-jest',
      testEnvironment: 'node',
    };
    ```
  </Tab>
</Tabs>

<div id="pre-user-registration-access-control-and-user-metadata-setup">
  ## ユーザー登録前のアクセス制御とユーザーメタデータの設定
</div>

次の Action の例では、ユーザーのメールアドレスのドメインが禁止対象かどうかを確認し、一致した場合は `api.access.deny()` を呼び出します。一致しない場合は、Custom Prompts の追加フィールド経由で氏名が提供されているかどうかを確認し、提供されていればユーザープロファイルの `user_metadata` に氏名を設定します。提供されていない場合は、Universal Login にバリデーションエラーを返します。

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    /** @import {Event, PreUserRegistrationAPI} from "@auth0/actions/pre-user-registration/v2" */

    /**
    * PreUserRegistration フローの実行中に呼び出されるハンドラー。
    *
    * @param {Event} event - 登録を試行しているコンテキストとユーザーの詳細。
    * @param {PreUserRegistrationAPI} api - サインアップの動作を変更するために使用できるメソッドを持つインターフェース。
    */
    exports.onExecutePreUserRegistration = async (event, api) => {
      const user = event.user;

      if (user.email?.endsWith('@example.com')) {
        api.access.deny('forbidden', 'Forbidden email domain')
        return;
      }

      const fullName = event.request.body['ulp-fullName'];

      if (fullName === undefined) {
        api.validation.error('invalid_payload', 'Missing full name');
        return;
      }

      api.user.setUserMetadata('full_name', fullName);
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import type { Event, PreUserRegistrationAPI } from '@auth0/actions/pre-user-registration/v2';

    /**
    * PreUserRegistration フローの実行中に呼び出されるハンドラー。
    *
    * @param {Event} event - 登録を試行しているコンテキストとユーザーの詳細。
    * @param {PreUserRegistrationAPI} api - サインアップの動作を変更するために使用できるメソッドを持つインターフェース。
    */
    exports.onExecutePreUserRegistration = async (event: Event, api: PreUserRegistrationAPI) => {
      const user = event.user;

      if (user.email?.endsWith('@example.com')) {
        api.access.deny('forbidden', 'Forbidden email domain')
        return;
      }

      const fullName = event.request.body['ulp-fullName'];

      if (fullName === undefined) {
        api.validation.error('invalid_payload', 'Missing full name');
        return;
      }

      api.user.setUserMetadata('full_name', fullName);
    };
    ```
  </Tab>
</Tabs>

このユニットテストでは、`event` オブジェクトと `api` オブジェクトをモック化し、コードカバレッジを最大化するためにいくつかの検証を行います。

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const { onExecutePreUserRegistration } = require('./preUserRegistration');

    describe('onExecutePreUserRegistration', () => {
      const mockApi = {
        access: {
          deny: jest.fn(),
        },
        user: {
          setUserMetadata: jest.fn(),
        },
        validation: {
          error: jest.fn(),
        },
      };

      beforeEach(() => {
        jest.resetAllMocks();
      });

      afterEach(() => {
        jest.resetAllMocks();
      });

      it('forbids email domain', async () => {
        const mockEvent = {
          user: {
            email: 'johndoe@example.com',
          }
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).toHaveBeenCalledWith('forbidden', 'Forbidden email domain');
        expect(mockApi.validation.error).not.toHaveBeenCalled();
        expect(mockApi.user.setUserMetadata).not.toHaveBeenCalled();
      });

      it('allows email domain without full name', async () => {
        const mockEvent = {
          request: {
            body: {},
          },
          user: {
            email: 'johndoe@test.com',
          },
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).not.toHaveBeenCalled();
        expect(mockApi.validation.error).toHaveBeenCalledWith('invalid_payload', 'Missing full name');
        expect(mockApi.user.setUserMetadata).not.toHaveBeenCalled();
      });

      it('allows email domain with full name', async () => {
        const mockEvent = {
          request: {
            body: {
              'ulp-fullName': 'John Doe'
            },
          },
          user: {
            email: 'johndoe@test.com',
          },
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).not.toHaveBeenCalled();
        expect(mockApi.validation.error).not.toHaveBeenCalled();
        expect(mockApi.user.setUserMetadata).toHaveBeenCalledWith('full_name', 'John Doe');
      });
    });
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const { onExecutePreUserRegistration } = require('./preUserRegistration');

    describe('onExecutePreUserRegistration', () => {
      const mockApi = {
        access: {
          deny: jest.fn(),
        },
        user: {
          setUserMetadata: jest.fn(),
        },
        validation: {
          error: jest.fn(),
        },
      };

      beforeEach(() => {
        jest.resetAllMocks();
      });

      afterEach(() => {
        jest.resetAllMocks();
      });

      it('forbids email domain', async () => {
        const mockEvent = {
          user: {
            email: 'johndoe@example.com',
          }
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).toHaveBeenCalledWith('forbidden', 'Forbidden email domain');
        expect(mockApi.validation.error).not.toHaveBeenCalled();
        expect(mockApi.user.setUserMetadata).not.toHaveBeenCalled();
      });

      it('allows email domain without full name', async () => {
        const mockEvent = {
          request: {
            body: {},
          },
          user: {
            email: 'johndoe@test.com',
          },
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).not.toHaveBeenCalled();
        expect(mockApi.validation.error).toHaveBeenCalledWith('invalid_payload', 'Missing full name');
        expect(mockApi.user.setUserMetadata).not.toHaveBeenCalled();
      });

      it('allows email domain with full name', async () => {
        const mockEvent = {
          request: {
            body: {
              'ulp-fullName': 'John Doe'
            },
          },
          user: {
            email: 'johndoe@test.com',
          },
        };

        await onExecutePreUserRegistration(mockEvent, mockApi);

        expect(mockApi.access.deny).not.toHaveBeenCalled();
        expect(mockApi.validation.error).not.toHaveBeenCalled();
        expect(mockApi.user.setUserMetadata).toHaveBeenCalledWith('full_name', 'John Doe');
      });
    });
    ```
  </Tab>
</Tabs>

<div id="custom-email-provider-and-http-request">
  ## カスタムメールプロバイダーとHTTPリクエスト
</div>

次の Action の例では、外部サービスに対して HTTP リクエストでメッセージを送信し、リクエスト エラーが発生した場合は通知を破棄するよう処理しています。外部サービスの URL と認証用 API キーにはシークレットを使用します。

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    /** @import {Event, CustomEmailProviderAPI} from "@auth0/actions/custom-email-provider/v1" */

    /**
    * メール通知の送信時に実行されるハンドラー
    *
    * @param {Event} event - ユーザーと、そのユーザーのログイン時のコンテキストに関する詳細。
    * @param {CustomEmailProviderAPI} api - メール通知の送信動作を変更するためのメソッドとユーティリティ。
    */
    exports.onExecuteCustomEmailProvider = async (event, api) => {
      const notification = event.notification;
      const message = {
        body: notification.html
      };

      try {
        await fetch(event.secrets.URL, {
          method: 'POST',
          headers: {
            'X-API-Key': event.secrets.API_KEY,
          },
          body: JSON.stringify(message),
        });
      } catch (err) {
        api.notification.drop('External service failure');
      }
    }

    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    /** @import {Event, CustomEmailProviderAPI} from "@auth0/actions/custom-email-provider/v1" */

    /**
    * メール通知の送信時に実行されるハンドラー
    *
    * @param {Event} event - ユーザーと、そのユーザーのログイン時のコンテキストに関する詳細。
    * @param {CustomEmailProviderAPI} api - メール通知の送信動作を変更するためのメソッドとユーティリティ。
    */
    exports.onExecuteCustomEmailProvider = async (event, api) => {
      const notification = event.notification;
      const message = {
        body: notification.html
      };

      try {
        await fetch(event.secrets.URL, {
          method: 'POST',
          headers: {
            'X-API-Key': event.secrets.API_KEY,
          },
          body: JSON.stringify(message),
        });
      } catch (err) {
        api.notification.drop('External service failure');
      }
    }

    ```
  </Tab>
</Tabs>

ユニットテストでは、コード カバレッジを最大化するために、`fetch` 関数に加えて event オブジェクトと api オブジェクトをモック化し、いくつかの検証を行います。

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const { onExecuteCustomEmailProvider } = require('./customEmailProvider');

    describe('onExecuteCustomEmailProvider', () => {
      const mockApi = {
        notification: {
          drop: jest.fn(),
        },
      };

      const mockEvent = {
        notification: {
          html: '<h1>Hello world</h1>',
        },
        secrets: {
          URL: 'https://example.com/service',
          API_KEY: 'ApiKeySecret1234.',
        },
        user: {
          email: 'johndoe@example.com',
        },
      };

      beforeEach(() => {
        jest.resetAllMocks();
      });

      afterEach(() => {
        jest.resetAllMocks();
      });

      it('succeeds on external service request', async () => {
        jest.spyOn(global, 'fetch').mockImplementationOnce(() => Promise.resolve({
          ok: true,
          status: 200,
          json: async () => ({ message: 'Success!' }),
        }));

        await onExecuteCustomEmailProvider(mockEvent, mockApi);

        expect(global.fetch).toHaveBeenCalled();
        expect(mockApi.notification.drop).not.toHaveBeenCalled();
      });

      it('fails on external service request', async () => {
        jest.spyOn(global, 'fetch').mockImplementationOnce(() => Promise.reject({
          ok: false,
          status: 500,
          json: async () => ({ error: 'Server Error' }),
        }));

        await onExecuteCustomEmailProvider(mockEvent, mockApi);

        expect(mockApi.notification.drop).toHaveBeenCalledWith('External service failure');
      });
    });

    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const { onExecuteCustomEmailProvider } = require('./customEmailProvider');

    describe('onExecuteCustomEmailProvider', () => {
      const mockApi = {
        notification: {
          drop: jest.fn(),
        },
      };

      const mockEvent = {
        notification: {
          html: '<h1>Hello world</h1>',
        },
        secrets: {
          URL: 'https://example.com/service',
          API_KEY: 'ApiKeySecret1234.',
        },
        user: {
          email: 'johndoe@example.com',
        },
      };

      beforeEach(() => {
        jest.resetAllMocks();
      });

      afterEach(() => {
        jest.resetAllMocks();
      });

      it('succeeds on external service request', async () => {
        jest.spyOn(global, 'fetch').mockImplementationOnce(() => Promise.resolve({
          ok: true,
          status: 200,
          json: async () => ({ message: 'Success!' }),
        } as Response));

        await onExecuteCustomEmailProvider(mockEvent, mockApi);

        expect(global.fetch).toHaveBeenCalled();
        expect(mockApi.notification.drop).not.toHaveBeenCalled();
      });

      it('fails on external service request', async () => {
        jest.spyOn(global, 'fetch').mockImplementationOnce(() => Promise.reject({
          ok: false,
          status: 500,
          json: async () => ({ error: 'Server Error' }),
        } as Response));

        await onExecuteCustomEmailProvider(mockEvent, mockApi);

        expect(mockApi.notification.drop).toHaveBeenCalledWith('External service failure');
      });
    });

    ```
  </Tab>
</Tabs>

`@auth0/actions` の詳細については、[https://www.npmjs.com/package/@auth0/actions](https://www.npmjs.com/package/@auth0/actions) を参照してください。

Actions の作成方法について詳しくは、[初めての Action を作成する](/ja/docs/customize/actions/write-your-first-action) を参照してください。
