- 検証・テスト
契約テスト(Pact)入門|コンシューマー駆動の流れとコード例
契約テストは、API を呼ぶ側(コンシューマー)と提供する側(プロバイダー)が「このリクエストには、この形のレスポンスを返す」という約束(契約)を守れているかを、別々に確かめるテストです。 両方のサービスを同時に起動せずに、つなぎ目の食い違いを見つけられます。
Pact は「コンシューマー駆動」の契約テストのツールです。 呼ぶ側のテストから契約(pact ファイル)を作り、提供する側はその契約どおりに応答できるかを検証します。
| 順番 | 誰が | すること | 生まれるもの |
|---|---|---|---|
| 1 | コンシューマー | Pact のモックサーバーに向けて、API クライアントのテストを書く | テストの結果 |
| 2 | コンシューマー | テストが通ると、使ったリクエストとレスポンスが記録される | pact ファイル(JSON) |
| 3 | 両方 | pact ファイルを Pact Broker に登録して共有する | 契約の版の記録 |
| 4 | プロバイダー | 本物の API を起動し、pact ファイルのリクエストを再生して応答を比べる | 検証の結果 |
| 5 | 両方 | デプロイ前に can-i-deploy で、相手の版と検証済みかを確かめる |
デプロイしてよいかの判定 |
確認環境:@pact-foundation/pact 17.1.4(内部の pact-core 20.2.0、Pact 仕様 4.0)、Vitest 5.0.4、Express 5.3.0、Node.js 22.22.2(Linux x64)で、手順 1・2・4 を実行しました。Pact のネイティブライブラリ(libpact_ffi.so)は npm の @pact-foundation/pact-core-linux-x64-glibc に入っており、追加のダウンロードなしで動きました。Pact Broker と can-i-deploy は、Pact のドキュメントと @pact-foundation/pact-cli 18.1.2 のヘルプで確かめました(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「契約テスト(Pact)入門|コンシューマー駆動の流れとコード例」
契約テストが解決すること
マイクロサービスや、フロントエンドとバックエンドを別のチームが作る構成では、API の項目名・型・ステータスコードのずれがよく起きます。
| よくあるずれ | 単体テスト | E2E テスト | 契約テスト |
|---|---|---|---|
プロバイダーが項目名を変えた(name → fullName) |
両側のテストは通る | 気づける(遅い) | プロバイダーの検証で気づける |
| 型が変わった(数値 → 文字列) | 両側のテストは通る | 画面次第 | 気づける |
| コンシューマーが別の URL を呼んでいる | モック次第 | 気づける | コンシューマーのテストで気づける |
| 業務の流れ全体が正しいか | 確かめない | 確かめる | 確かめない |
単体テストのモックは「自分が思っている API」を真似るだけなので、相手が変わっても通り続けます。契約テストは、そのモックの中身を契約として相手に渡し、相手の本物で確かめる、というしくみです。
準備
npm init -y
npm pkg set type=module
npm i -D @pact-foundation/pact vitest
インストールすると、OS に合ったネイティブライブラリのパッケージ(Linux x64 なら @pact-foundation/pact-core-linux-x64-glibc)も入ります。package.json の engines では、Pact JS 17 は Node.js 22 以上です。
@pact-foundation/pact@17.1.4
├── @pact-foundation/pact-core@20.2.0
└── @pact-foundation/pact-core-linux-x64-glibc
└── prebuilds/linux-x64/libpact_ffi.so, node.napi.node
Express の基本は、関連記事「Node.js Express入門|Express5対応の最小APIをコピペで作る」で扱っています。
手順1・2:コンシューマーテストを書いて pact ファイルを作る
テストする API クライアント
注文画面(OrderWeb)が、ユーザー API(UserService)からユーザーを取る関数です。
// src/userClient.js
export async function getUser(baseUrl, id) {
const res = await fetch(`${baseUrl}/users/${id}`, {
headers: { Accept: 'application/json' },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`unexpected status ${res.status}`);
const body = await res.json();
return { id: body.id, name: body.name, email: body.email };
}
接続先の URL を引数で受け取る形にしておくと、テストでは Pact のモックサーバーに向けられます。
コンシューマーテスト(Vitest)
// test/user.consumer.test.js
import path from 'node:path';
import { describe, it, expect } from 'vitest';
import { PactV4, MatchersV3 } from '@pact-foundation/pact';
import { getUser } from '../src/userClient.js';
const { like, integer, regex } = MatchersV3;
const provider = new PactV4({
consumer: 'OrderWeb',
provider: 'UserService',
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: 'warn',
});
describe('UserService の契約', () => {
it('ユーザーが存在するとき 200 と利用者情報を返す', async () => {
await provider
.addInteraction()
.given('ユーザー 1 が存在する', { id: 1 }) // プロバイダーの状態
.uponReceiving('ユーザー 1 の取得') // やり取りの名前
.withRequest('GET', '/users/1', (b) => {
b.headers({ Accept: 'application/json' });
})
.willRespondWith(200, (b) => {
b.headers({ 'Content-Type': 'application/json' });
b.jsonBody({
id: integer(1), // 整数なら何でもよい
name: like('山田 太郎'), // 文字列なら何でもよい
email: regex('^[^@]+@[^@]+$', 'taro@example.com'),
});
})
.executeTest(async (mockServer) => {
const user = await getUser(mockServer.url, 1);
expect(user).toEqual({ id: 1, name: '山田 太郎', email: 'taro@example.com' });
});
});
it('ユーザーが存在しないとき 404 を返す', async () => {
await provider
.addInteraction()
.given('ユーザー 999 は存在しない')
.uponReceiving('存在しないユーザーの取得')
.withRequest('GET', '/users/999', (b) => {
b.headers({ Accept: 'application/json' });
})
.willRespondWith(404)
.executeTest(async (mockServer) => {
const user = await getUser(mockServer.url, 999);
expect(user).toBeNull();
});
});
});
npx vitest run test/user.consumer.test.js
Test Files 1 passed (1)
Tests 2 passed (2)
Duration 777ms (import 86%, transform 7%, tests 6%, worker 1%)
| 部品 | 意味 |
|---|---|
PactV4 |
Pact 仕様 4 の契約を作るクラス(Pact という名前でも export されている) |
given(状態, 引数) |
プロバイダー側で用意してほしい状態。検証のときに名前で呼び分ける |
uponReceiving(名前) |
やり取りの名前。検証の出力にこの名前が出る |
withRequest / willRespondWith |
期待するリクエストと、返ってきてほしいレスポンス |
executeTest |
モックサーバーを起動し、関数の中で実際に API クライアントを呼ぶ |
README では PactV3 の例が載っています。書き方はほぼ同じで、PactV4 は addInteraction() から始めます。
コンシューマーのテストが失敗する例
API クライアントが誤って /user/1(s が無い)を呼ぶと、モックサーバーが次のように知らせました。
ERROR (14474): pact@17.1.4: Test failed for the following reasons:
Mock server failed with the following mismatches:
0) The following request was not expected:
Method: GET
Path: /user/1
...
1) The following request was expected but not received:
Method: GET
Path: /users/1
Headers:
Accept: application/json
モックサーバーは、期待していないリクエストには 500 を返します。このときクライアント側は Error: unexpected status 500 で失敗しました。テストが失敗したときは、pact ファイルは書き出されません。
できあがった pact ファイル
pacts/OrderWeb-UserService.json が作られました(2,305 バイト)。1つ目のやり取りの部分です。
{
"consumer": { "name": "OrderWeb" },
"interactions": [
{
"description": "ユーザー 1 の取得",
"pending": false,
"providerStates": [
{ "name": "ユーザー 1 が存在する", "params": { "id": 1 } }
],
"request": {
"headers": { "Accept": ["application/json"] },
"method": "GET",
"path": "/users/1"
},
"response": {
"body": {
"content": { "email": "taro@example.com", "id": 1, "name": "山田 太郎" },
"contentType": "application/json",
"encoded": false
},
"headers": { "Content-Type": ["application/json"] },
"matchingRules": {
"body": {
"$.email": { "combine": "AND", "matchers": [{ "match": "regex", "regex": "^[^@]+@[^@]+$" }] },
"$.id": { "combine": "AND", "matchers": [{ "match": "integer" }] },
"$.name": { "combine": "AND", "matchers": [{ "match": "type" }] }
}
},
"status": 200
},
"type": "Synchronous/HTTP"
}
],
"metadata": {
"pact-js": { "version": "17.1.4" },
"pactRust": { "ffi": "0.5.8", "models": "1.3.16" },
"pactSpecification": { "version": "4.0" }
},
"provider": { "name": "UserService" }
}
matchingRules に、マッチャーで書いた「どこまで同じならよいか」が入ります。 値そのもの(山田 太郎)は例で、検証では型や正規表現で比べられます。
よく使うマッチャー
MatchersV3 にある主なものです。
| マッチャー | 一致の条件 | 使いどころ |
|---|---|---|
like(例) |
型が同じ | 名前・説明など値が毎回違う項目 |
integer(例)・decimal(例)・number(例) |
整数・小数・数値 | ID・金額 |
boolean(例)・string(例) |
真偽値・文字列 | フラグ |
regex(正規表現, 例) |
正規表現に合う | 形式が決まった文字列 |
eachLike(例)・atLeastOneLike(例) |
配列の各要素が例と同じ形 | 一覧 API |
datetime(形式, 例)・iso8601DateTime(例) |
日時の形式に合う | 作成日時 |
uuid(例)・email(例) |
UUID・メールの形式 | ID・連絡先 |
includes(文字列) |
その文字列を含む | エラーメッセージ |
fromProviderState(式, 例) |
プロバイダーの状態から値を入れる | 検証時に決まる ID |
値をそのまま書く(マッチャーを使わない)と、検証では完全一致で比べます。 一致を厳しくしすぎると、プロバイダーの小さな変更でも検証が落ちるので、コンシューマーが本当に使う項目と型だけを書きます。
手順4:プロバイダーで検証する
プロバイダーの API
// src/provider.js
import express from 'express';
export const users = new Map();
export function createApp() {
const app = express();
app.get('/users/:id', (req, res) => {
const user = users.get(Number(req.params.id));
if (!user) return res.status(404).end();
res.json(user);
});
return app;
}
検証テスト
// test/user.provider.test.js
import path from 'node:path';
import { describe, it, beforeAll, afterAll } from 'vitest';
import { Verifier } from '@pact-foundation/pact';
import { createApp, users } from '../src/provider.js';
let server;
let port;
beforeAll(async () => {
server = createApp().listen(0); // 空いているポートで起動
await new Promise((r) => server.once('listening', r));
port = server.address().port;
});
afterAll(() => server.close());
describe('UserService の契約の検証', () => {
it('OrderWeb の契約を満たす', () => {
return new Verifier({
provider: 'UserService',
providerBaseUrl: `http://127.0.0.1:${port}`,
pactUrls: [path.resolve(process.cwd(), 'pacts/OrderWeb-UserService.json')],
// given(...) の名前ごとに、テストデータを用意する
stateHandlers: {
'ユーザー 1 が存在する': async (params) => {
users.clear();
users.set(params.id, { id: params.id, name: '佐藤 花子', email: 'hanako@example.com' });
},
'ユーザー 999 は存在しない': async () => {
users.clear();
},
},
logLevel: 'warn',
}).verifyProvider();
});
});
Verifying a pact between OrderWeb and UserService
ユーザー 1 の取得 (0s loading, 36ms verification)
Given ユーザー 1 が存在する
returns a response which
has status code 200 (OK)
includes headers
"Content-Type" with value "application/json" (OK)
has a matching body (OK)
存在しないユーザーの取得 (0s loading, 7ms verification)
Given ユーザー 999 は存在しない
returns a response which
has status code 404 (OK)
has a matching body (OK)
Test Files 1 passed (1)
Tests 1 passed (1)
確かめられたことです。
- プロバイダーは名前に
佐藤 花子を返しましたが、通りました。 契約ではlike('山田 太郎')(文字列なら何でもよい)としたためです。 - Express は
Content-Type: application/json; charset=utf-8を返しますが、application/jsonとの比較で OK になりました。 - レスポンスに契約に無い項目(
phone)を足しても、検証は通りました。 コンシューマーが使わない項目を増やすことは、契約を壊しません。
stateHandlers のキーは、コンシューマーの given(...) の文字列と完全に同じにします。params には given の第2引数({ id: 1 })が渡されます。
契約を破ったとき
プロバイダーが、name を fullName に変え、id を文字列にし、email を不正な値にしたときの出力です。
ユーザー 1 の取得 (0s loading, 30ms verification)
Given ユーザー 1 が存在する
returns a response which
has status code 200 (OK)
includes headers
"Content-Type" with value "application/json" (OK)
has a matching body (FAILED)
Failures:
1) Verifying a pact between OrderWeb and UserService Given ユーザー 1 が存在する - ユーザー 1 の取得
1.1) has a matching body
$ -> Actual map is missing the following keys: name
$.email -> Expected 'hanako' to match '^[^@]+@[^@]+$'
$.id -> Expected '1' (String) to be an integer
There were 1 pact failures
| 出力 | 読み方 |
|---|---|
Actual map is missing the following keys: name |
コンシューマーが使う name が無い |
Expected 'hanako' to match '^[^@]+@[^@]+$' |
regex の条件に合わない |
Expected '1' (String) to be an integer |
integer のはずが文字列 |
expected 200 but was 403 |
ステータスコードが違う |
どのコンシューマーの、どのやり取りの、どの項目かが分かるので、プロバイダーの開発者は相手のコードを読まずに直す場所を決められます。
プロキシの環境変数に注意
HTTPS_PROXY が設定されたシェルで検証を実行すると、プロバイダーへのリクエストがプロキシに送られ、すべてのやり取りが expected 200 but was 403 で失敗しました。NO_PROXY に 127.0.0.1 があっても同じで、検証のときだけ HTTPS_PROXY を外すと通りました。
env -u HTTPS_PROXY -u https_proxy npx vitest run test/user.provider.test.js
手順3・5:Pact Broker と can-i-deploy
Pact Broker の役割
Pact Broker は、pact ファイルと検証の結果を、版(バージョン)ごとに保存して共有するサーバーです。 オープンソース版(Docker イメージ)と、SaaS の PactFlow があります。
| 役割 | 内容 |
|---|---|
| 契約の共有 | コンシューマーが登録した pact ファイルを、プロバイダーの CI が取りに行く |
| 版の対応表(Pact Matrix) | 「コンシューマーの版 × プロバイダーの版」で検証が通ったかを記録する |
| デプロイの記録 | どの版がどの環境(test・production)に出ているかを記録する |
| デプロイ前の判定 | can-i-deploy で、出してよいかを答える |
| Webhook | 契約が変わったら、プロバイダーの検証ジョブを起動する |
pact ファイルの登録(コンシューマーの CI)
npm の @pact-foundation/pact-cli(18.1.2)を入れると、pact broker コマンドが使えます。
npm i -D @pact-foundation/pact-cli
npx pact broker publish ./pacts \
--broker-base-url "$PACT_BROKER_BASE_URL" \
--broker-token "$PACT_BROKER_TOKEN" \
--consumer-app-version "$GIT_SHA" \
--branch "$GIT_BRANCH"
Broker から取って検証する(プロバイダーの CI)
pactUrls の代わりに Broker の情報を渡します(オプション名は pact-core の型定義で確認)。
new Verifier({
provider: 'UserService',
providerBaseUrl: `http://127.0.0.1:${port}`,
pactBrokerUrl: process.env.PACT_BROKER_BASE_URL,
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
// どの版の契約を検証するか
consumerVersionSelectors: [
{ mainBranch: true }, // コンシューマーの main ブランチの最新
{ matchingBranch: true }, // 同じ名前のブランチ
{ deployedOrReleased: true }, // どこかの環境に出ている版
],
enablePending: true, // 新しい契約で落ちてもビルドを止めない
publishVerificationResult: process.env.CI === 'true',
providerVersion: process.env.GIT_SHA,
providerVersionBranch: process.env.GIT_BRANCH,
stateHandlers: { /* 上と同じ */ },
}).verifyProvider();
| オプション | 意味 |
|---|---|
consumerVersionSelectors |
検証する契約の版を選ぶ条件 |
enablePending |
まだ一度も検証が通っていない新しい契約は、失敗してもビルドを失敗にしない |
publishVerificationResult |
検証の結果を Broker に送る(CI のときだけにするのが一般的) |
providerVersion・providerVersionBranch |
結果をどの版として記録するか |
can-i-deploy
pact broker can-i-deploy --help の説明では、指定した版が、その環境にすでに出ている相手の各版と、検証に成功しているかを確かめ、終了コード 0 か 1 を返します。
# デプロイ前:この版を production に出してよいか
npx pact broker can-i-deploy \
--pacticipant OrderWeb --version "$GIT_SHA" \
--to-environment production \
--broker-base-url "$PACT_BROKER_BASE_URL" --broker-token "$PACT_BROKER_TOKEN"
# デプロイ後:production に出したことを記録する
npx pact broker record-deployment \
--pacticipant OrderWeb --version "$GIT_SHA" \
--environment production \
--broker-base-url "$PACT_BROKER_BASE_URL" --broker-token "$PACT_BROKER_TOKEN"
ドキュメントの Pact Matrix の例です(Foo がコンシューマー、Bar がプロバイダー、production には Bar 56 が出ている)。
| Foo の版 | Bar の版 | 検証 |
|---|---|---|
| 22 | 56(production) | 成功 |
| 23 | 56(production) | 成功 |
| 23 | 57 | 失敗 |
| 24 | 58 | 成功 |
| 25 | 58 | 失敗 |
この表では、Foo 23 は production の Bar 56 と検証済みなので「出してよい」、Foo 24 は Bar 56 との検証が無いので「出してはいけない」と判定されます。
ヘルプにある使い方の注意です。
| 項目 | 内容 |
|---|---|
| 環境の作成 | test と production は最初からある。ほかは create-environment で作る |
| デプロイの記録 | 出した後に record-deployment(入れ替える)か record-release(並べて残す) |
| 結果を待つ | 検証が終わっていないときは --retry-while-unknown と --retry-interval で待つ |
| タグ | 以前のタグによる指定は、環境・デプロイの記録に置き換わっている |
CI の流れ
| ジョブ | コンシューマー(OrderWeb) | プロバイダー(UserService) |
|---|---|---|
| プルリクエスト | テスト → publish → can-i-deploy |
テスト → Broker の契約で検証 → 結果を送る |
| 契約が変わったとき | — | Webhook で検証ジョブが起動 |
| デプロイ | can-i-deploy → デプロイ → record-deployment |
同左 |
GitHub Actions の基本は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。
OpenAPI との違い
| 比べる点 | Pact(コンシューマー駆動) | OpenAPI(仕様書) |
|---|---|---|
| 誰が書くか | コンシューマーがテストとして書く | 多くはプロバイダーが書く |
| 何を表すか | 実際に使っているやり取りだけ | API の全体(使われない項目も含む) |
| 形 | テストから生まれる JSON | YAML・JSON の仕様書 |
| 確かめ方 | プロバイダーの本物で再生して比べる | 仕様と実装の一致は別の手段で確かめる |
| 分かること | どの項目を誰が使っているか(消してよいか) | API で何ができるか |
| 向く場面 | 社内のサービス同士、相手が分かっている | 公開 API、相手が分からない |
OpenAPI は「API で何ができるか」、Pact は「相手が実際に何を使っているか」を表します。 両方を使う場合、OpenAPI で設計を合意し、Pact でつなぎ目を守る、という分け方ができます。PactFlow には、OpenAPI とコンシューマーの契約を突き合わせる「双方向契約テスト(Bi-Directional Contract Testing)」もあります。OpenAPI の書き方は、関連記事「API設計書の書き方|Markdown・OpenAPIテンプレートと記入例」で扱っています。
E2E テストとの使い分け
| 比べる点 | 契約テスト | E2E テスト |
|---|---|---|
| 確かめること | サービス間の約束(項目・型・ステータス) | 利用者の操作から結果までの流れ |
| 起動するもの | 片方だけ(相手はモック・契約) | すべてのサービスとデータベース |
| 速さ | 単体テストに近い(今回 1 秒未満) | 遅い(数分〜) |
| 失敗したときの場所 | どのやり取りのどの項目か出る | どこで壊れたか調べる |
| 業務ルールの正しさ | 確かめない | 確かめる |
| 向く数 | やり取りごとに多く | 大事な流れに絞って少なく |
契約テストは E2E テストを置き換えるものではありません。 サービス間のつなぎ目の確認を契約テストに移すと、E2E テストを「大事な業務の流れ」に絞れます。E2E テストの書き方は、関連記事「Playwright入門|E2Eテストの書き方とCI実行まで実行結果つきで解説」で扱っています。API 単体の観点は、関連記事「APIテストの観点チェックリスト|正常系・異常系・認可・境界値」で扱っています。
契約を書くときの注意
| 注意 | 理由 |
|---|---|
| コンシューマーが使う項目だけを書く | 使わない項目まで書くと、プロバイダーの変更で無駄に落ちる |
| 値はマッチャーで型を決める | 完全一致だとテストデータが変わるだけで落ちる |
given の名前は業務の言葉で |
プロバイダーが stateHandlers を書くときの手がかりになる |
| 異常系(404・400)も書く | コンシューマーがエラーをどう扱うかも約束の一部 |
| 業務ルールは書かない | 「会員なら1割引き」はプロバイダーの単体テストで確かめる |
よくある質問
Q. pact ファイルはどこに置きますか。
A. 小さく試すときはリポジトリやファイル共有でも動きます(この記事では pactUrls でファイルを直接指定)。複数の版と環境を扱うなら、Pact Broker に置くと can-i-deploy が使えます。
Q. プロバイダーが Java(Spring Boot)でも使えますか。 A. 使えます。pact ファイルは言語に依存しない JSON で、Pact JVM などで検証できます。Spring Boot の API は、関連記事「Spring BootでREST APIを作る|入力チェック・エラー処理・テストまで」で扱っています。
Q. メッセージ(キュー)のやり取りにも使えますか。
A. Pact JS には、非同期メッセージ用の addAsynchronousInteraction() や MessageConsumerPact があります。
Q. 検証で has status code 200 (FAILED) と 403 が出ます。
A. プロバイダーの認証やプロキシが原因のことがあります。認証が要る API は、requestFilter などでヘッダーを足す方法がドキュメントにあります。プロキシは上の「プロキシの環境変数に注意」を見てください。
まとめ
- 契約テストは、呼ぶ側と提供する側の約束を、両方を同時に起動せずに確かめる
- Pact はコンシューマーのテストから pact ファイルを作り、プロバイダーで再生して検証する
- マッチャー(
like・integer・regexなど)で、値ではなく型や形式で比べる - 検証の失敗は、
Actual map is missing the following keys: nameのように項目単位で出る - Pact Broker は契約と検証の結果を版ごとに保存し、
can-i-deployがデプロイしてよいかを終了コードで答える - OpenAPI は API の全体、Pact は実際に使われる部分。E2E テストは業務の流れに絞る
参考・出典
確認日はいずれも 2026年10月13日です。
- Pact Docs「Introduction」:https://docs.pact.io/
- Pact Docs「Consumer Tests(JavaScript)」:https://docs.pact.io/implementation_guides/javascript/docs/consumer
- Pact Docs「Provider Verification(JavaScript)」:https://docs.pact.io/implementation_guides/javascript/docs/provider
- Pact Docs「Can I Deploy」:https://docs.pact.io/pact_broker/can_i_deploy
- Pact Docs「Pact Broker」:https://docs.pact.io/pact_broker
- pact-js(GitHub):https://github.com/pact-foundation/pact-js
- @pact-foundation/pact(npm):https://www.npmjs.com/package/@pact-foundation/pact
- PactFlow「Bi-Directional Contract Testing」:https://docs.pactflow.io/docs/bi-directional-contract-testing
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「契約テスト(Pact)入門|コンシューマー駆動の流れとコード例」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。