ミューテーションテストは、本番のコードにわざと小さなバグ(ミュータント)を入れて、テストがそのバグに気づけるかを確かめる方法です。 テストが失敗すればミュータントは「KILLED(検出できた)」、テストが通ってしまえば「SURVIVED(生き残った)」です。生き残ったミュータントは、テストが確かめていない振る舞いの目印になります。

Java では PIT(pitest) が広く使われています。この記事のサンプルでは、行カバレッジ 100% のテストでも、9 個のミュータントのうち 2 個が生き残りました。

>> Line Coverage (for mutated classes only): 11/11 (100%)
>> Generated 9 mutations Killed 7 (78%)
>> Mutations with no coverage 0. Test strength 78%
用語 意味
ミュータント 1か所だけ書き換えたコード(例:>= を > に)
ミューテーター 書き換えの種類(例:CONDITIONALS_BOUNDARY)
KILLED テストが失敗した = バグを検出できた
SURVIVED テストが全部通った = バグを見逃した
Mutation Coverage 全ミュータントのうち検出できた割合
Test Strength テストが通る行にあるミュータントのうち、検出できた割合

確認環境:PIT 1.30.0(GitHub のタグで最新の版)を、タグ 1.30.0 のソースから javac でビルドし、OpenJDK 21.0.10(Linux)でコマンドライン版を実行しました。テストは JUnit 4.13.2 と、pitest-junit5-plugin 1.2.3(ソースからビルド)+ JUnit Jupiter 5.9.1 の両方で動かし、同じ結果を確かめています。Maven プラグインの既定値は pitest-maven のソース、Gradle の設定は gradle-pitest-plugin 1.19.0 の README とソースで確かめました。Python の mutmut は 3.8.0(pytest 9.1.1、Python 3.11)で実行しています(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ミューテーションテストの始め方|PIT(Java)の設定と結果の読み方」

ミューテーションテストのしくみ

PIT はソースではなく、コンパイル済みのバイトコードを書き換えます。 流れは次のとおりです。

順番 PIT がすること
1 テストを1回流し、どのテストがどの行を通るか(カバレッジ)を調べる
2 対象クラスのバイトコードから、ミュータントを作る
3 ミュータントごとに、その行を通るテストだけを流す
4 テストが1つでも失敗したら、そこで止めて KILLED とする
5 全部通ったら SURVIVED、どのテストも通らない行なら NO_COVERAGE

テストを全部流し直すのではなく、その行を通るテストだけを流すので、ミュータントの数のわりに速く終わります。サンプルでは 9 個のミュータントに対して、流したテストは合計 14 回でした(Ran 14 tests (1.56 tests per mutation))。

カバレッジとの違い(行カバレッジ100%でも見逃す例)

対象のコード

package com.example;

public interface AuditLog {
    void record(String message);
}
package com.example;

public class Cart {
    private final AuditLog audit;

    public Cart(AuditLog audit) {
        this.audit = audit;
    }

    /** 小計 5000 円以上なら送料無料、それ以外は 500 円 */
    public int shippingFee(int subtotal) {
        if (subtotal >= 5000) {
            return 0;
        }
        return 500;
    }

    /** 合計。会員は小計から 1 割引き */
    public int total(int subtotal, boolean member) {
        int sum = subtotal + shippingFee(subtotal);
        if (member) {
            sum = sum - subtotal / 10;
        }
        audit.record("total=" + sum);
        return sum;
    }
}

すべての行を通るテスト(JUnit 5)

package com.example;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;

class CartTest {
    private final List<String> logs = new ArrayList<>();
    private final Cart cart = new Cart(logs::add);

    @Test void freeShipping() { assertEquals(0, cart.shippingFee(6000)); }
    @Test void paidShipping() { assertEquals(500, cart.shippingFee(1000)); }
    @Test void memberTotal()  { assertEquals(5400, cart.total(6000, true)); }
    @Test void guestTotal()   { assertEquals(1500, cart.total(1000, false)); }
}

4件のテストはすべて通り、if の両方の道も通っています。行カバレッジは 100%(11/11)、分岐もすべて通っています。 それでも PIT の結果は次のとおりでした(HTML レポートの行ごとの表示)。

行 ソース ミューテーション 結果
12 if (subtotal >= 5000) { changed conditional boundary(>= → >) SURVIVED
12 同上 negated conditional(>= → <) KILLED(paidShipping)
15 return 500; replaced int return with 0 KILLED
20 int sum = subtotal + shippingFee(subtotal); Replaced integer addition with subtraction KILLED
21 if (member) { negated conditional KILLED
22 sum = sum - subtotal / 10; subtraction → addition、division → multiplication KILLED(2個)
24 audit.record("total=" + sum); removed call to com/example/AuditLog::record SURVIVED
25 return sum; replaced int return with 0 KILLED

生き残った2つの理由

ミュータント なぜテストが気づかなかったか
>= → >(12行目) テストの値が 6000 と 1000 で、境界の 5000 を使っていない。5000 以外では >= と > の結果が同じ
audit.record(...) の削除(24行目) テストは戻り値しか見ておらず、ログに書かれたかを確かめていない

カバレッジは「その行が実行されたか」、ミューテーションテストは「その行が間違っていたらテストが気づくか」を測ります。 行が実行されても、判定(assert)がその行の結果を見ていなければ、カバレッジは上がってもバグは見逃されます。カバレッジの測り方は、関連記事「JaCoCoの使い方|Gradle・Mavenでカバレッジを測りCIで基準を決める」で扱っています。

テストを直す

生き残った2つに対応するテストを足しました。

    // 追加 1:境界値(5000 ちょうどと 1 円下)
    @Test void boundary() {
        assertEquals(0, cart.shippingFee(5000));
        assertEquals(500, cart.shippingFee(4999));
    }

    // 追加 2:監査ログに記録されたことを確かめる
    @Test void recordsAudit() {
        cart.total(1000, false);
        assertEquals(List.of("total=1500"), logs);
    }

もう一度実行した結果です。

>> Line Coverage (for mutated classes only): 11/11 (100%)
>> Generated 9 mutations Killed 9 (100%)
>> Mutations with no coverage 0. Test strength 100%
>> Ran 15 tests (1.67 tests per mutation)

行カバレッジは 100% のまま変わらず、ミューテーションスコアだけが 78% → 100% に上がりました。 境界値の選び方は、関連記事「同値分割・境界値分析|テストケース表とpytestコード例で解説」で、ホワイトボックスの技法は、関連記事「テスト技法一覧|ブラックボックス・ホワイトボックスの種類と選び方早見表」で扱っています。

結果の読み方

コンソールの集計

ミューテーターごとの内訳が出ます(直す前の結果)。

> org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator
>> Generated 1 Killed 0 (0%)
> KILLED 0 SURVIVED 1 TIMED_OUT 0 NON_VIABLE 0
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0
> NO_COVERAGE 0 EQUIVALENT 0
--------------------------------------------------------------------------------
> org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator
>> Generated 1 Killed 0 (0%)
> KILLED 0 SURVIVED 1 TIMED_OUT 0 NON_VIABLE 0
...
================================================================================
- Statistics
================================================================================
>> Line Coverage (for mutated classes only): 11/11 (100%)
>> 1 tests examined
>> Generated 9 mutations Killed 7 (78%)
>> Mutations with no coverage 0. Test strength 78%
>> Ran 14 tests (1.56 tests per mutation)

HTML レポート

index.html の Project Summary には、次の4つの数字が並びます。

項目 直す前 直した後
Number of Classes 1 1
Line Coverage 100%(11/11) 100%(11/11)
Mutation Coverage 78%(7/9) 100%(9/9)
Test Strength 78%(7/9) 100%(9/9)

クラスのページでは、行の左にミュータントの数が出て、その下に 1. shippingFee : changed conditional boundary → SURVIVED のように1つずつの結果が並びます。ページの下には、行ごとの「Killed by(どのテストが検出したか)」「Covered by tests(その行を通るテスト)」と、使ったミューテーターの一覧(Active mutators)が出ます。

Mutation Coverage と Test Strength の違い:Mutation Coverage は全ミュータントが分母です。Test Strength は、テストが1つも通らない行(NO_COVERAGE)のミュータントを分母から除きます。2つの差が大きいときは、テストが通っていない行が多いことを表します。

結果の状態の一覧

PIT 1.30.0 のソース(DetectionStatus)にある状態です。「検出扱い」は Mutation Coverage で KILLED と同じく数えられるものです。

状態 意味 検出扱い
KILLED テストが失敗した はい
SURVIVED 通るテストはあったが、全部成功した いいえ
NO_COVERAGE その行を通るテストが無い いいえ
TIMED_OUT 時間切れ(無限ループになったなど) はい
MEMORY_ERROR メモリ不足で止まった はい
RUN_ERROR 実行中にエラーが起きた はい
NON_VIABLE 書き換えたクラスが読み込めなかった はい
NOT_STARTED・STARTED 途中で終わった いいえ

CSV・XML

--outputFormats に CSV・XML を足すと、mutations.csv・mutations.xml が出ます。生き残りだけを拾うときは CSV が扱いやすいです。

Cart.java,com.example.Cart,org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator,shippingFee,12,SURVIVED,none
Cart.java,com.example.Cart,org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator,total,24,SURVIVED,none

列は、ファイル・クラス・ミューテーター・メソッド・行・状態・検出したテストの順です。

ミューテーター一覧(既定のもの)

mutators を指定しないときに使われるのは、次の 11 個です(HTML レポートの Active mutators と、PIT 1.30.0 のソース Mutator.newDefaults() で確認)。

ミューテーター 書き換えの例 生き残ったときに足すテスト
CONDITIONALS_BOUNDARY < ↔ <=、> ↔ >= 境界ちょうどの値
NEGATE_CONDITIONALS == ↔ !=、< ↔ >= など 条件の true・false の両方
MATH + ↔ -、* ↔ / など 計算結果の値を確かめる
INCREMENTS i++ → i-- ループの回数・合計を確かめる
INVERT_NEGS -x → x 負の値の結果を確かめる
VOID_METHOD_CALLS 戻り値が void の呼び出しを消す 呼び出しの結果(副作用)を確かめる
PRIMITIVE_RETURNS int などの戻り値を 0 に 戻り値を確かめる
TRUE_RETURNS・FALSE_RETURNS boolean の戻り値を true・false に true・false の両方の場合
EMPTY_RETURNS 文字列を ""、コレクションを空に、Optional を空に 中身を確かめる
NULL_RETURNS オブジェクトの戻り値を null に null でないことを確かめる

グループを指定したとき

PIT のドキュメントでは、mutators にグループ名を書くと、そのグループの全部が有効になります。同じサンプルで比べました。

指定 作られた数 内訳の違い
指定なし 9 上の 11 個から作られる
DEFAULTS 9 1.30.0 では NEGATE_CONDITIONALS の代わりに REMOVE_CONDITIONALS_ORDER_ELSE・REMOVE_CONDITIONALS_EQUAL_ELSE が入った
STRONGER 11 DEFAULTS に REMOVE_CONDITIONALS_ORDER_IF・_EQUAL_IF・EXPERIMENTAL_SWITCH を足したもの
ALL 20 試験的なものも含めた全部

ドキュメントの表では DEFAULTS に NEGATE_CONDITIONALS が入っていますが、1.30.0 のソースの DEFAULTS グループは REMOVE_CONDITIONALS_*_ELSE を使います。 どちらも条件を逆向きにする書き換えで、サンプルでは検出の結果は同じでした。版を上げたときに数が変わることがあるので、CI では mutators を明示しておくと比べやすくなります。

既定に入っていない主なもの(ドキュメントより):

ミューテーター 書き換え
REMOVE_CONDITIONALS 条件を常に true・false にする
CONSTRUCTOR_CALLS new の結果を null にする
NON_VOID_METHOD_CALLS 戻り値のある呼び出しを消し、既定値(0・null など)にする
INLINE_CONSTS 定数を別の値にする(1 → 0 など)
EXPERIMENTAL_SWITCH switch の行き先を入れ替える

Maven の設定

<build>
  <plugins>
    <plugin>
      <groupId>org.pitest</groupId>
      <artifactId>pitest-maven</artifactId>
      <version>1.30.0</version>
      <dependencies>
        <!-- JUnit 5(JUnit Platform)のときに足す。JUnit 4 なら不要 -->
        <dependency>
          <groupId>org.pitest</groupId>
          <artifactId>pitest-junit5-plugin</artifactId>
          <version>1.2.3</version>
        </dependency>
      </dependencies>
      <configuration>
        <targetClasses>
          <param>com.example.*</param>
        </targetClasses>
        <targetTests>
          <param>com.example.*Test</param>
        </targetTests>
        <threads>4</threads>
        <outputFormats>
          <outputFormat>HTML</outputFormat>
          <outputFormat>XML</outputFormat>
        </outputFormats>
        <timestampedReports>false</timestampedReports>
        <mutationThreshold>80</mutationThreshold>
      </configuration>
    </plugin>
  </plugins>
</build>
# テストをコンパイルしてから実行する
./mvnw test-compile org.pitest:pitest-maven:mutationCoverage

# 2回目以降を速くする(前回の結果を使う)
./mvnw -DwithHistory test-compile org.pitest:pitest-maven:mutationCoverage

pitest-junit5-plugin の README には、プラグインはプロジェクトの依存ではなく、PIT のプラグインの依存(上の <dependencies>)に置くとあります。

pitest-maven 1.30.0 のソースにある主な設定の既定値です。

設定 既定値 意味
reportsDirectory ${project.build.directory}/pit-reports レポートの出力先
timestampedReports false true で日時のフォルダを作る
threads 1 並列に動かすスレッド数
timeoutFactor・timeoutConstant 1.25・3000 通常の時間×1.25+3000ミリ秒で時間切れ
mutationThreshold・coverageThreshold 0 下回ったらビルドを失敗させる(%)
maxSurviving -1 生き残りがこの数を超えたら失敗(-1 は使わない)
withHistory false 前回の結果を使って速くする
failWhenNoMutations true ミュータントが0個だとエラー

ゴール mutationCoverage の既定のフェーズは verify です。1.30.0 のソースにあるゴールは、ほかに report・report-aggregate・report-aggregate-module(マルチモジュールの結果をまとめる)です。pom.xml の書き方は、関連記事「Mavenコマンド一覧|pom.xml書き方とよく使うオプション」で扱っています。

基準を下回ったとき

mutationThreshold を 90 にして、直す前のテストで実行すると次のように止まりました(コマンドライン版の出力)。

Exception in thread "main" java.lang.RuntimeException: Mutation score of 78 is below threshold of 90

Gradle の設定(build.gradle.kts)

Gradle では、コミュニティのプラグイン gradle-pitest-plugin(info.solidsoft.pitest)を使います。

plugins {
    java
    id("info.solidsoft.pitest") version "1.19.0"
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(platform("org.junit:junit-bom:5.13.4"))
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
    useJUnitPlatform()
}

pitest {
    pitestVersion = "1.30.0"            // 書かないとプラグインの既定(1.19.0 では 1.22.1)
    junit5PluginVersion = "1.2.3"       // pitest-junit5-plugin を足し、testPlugin を junit5 にする
    targetClasses = setOf("com.example.*")
    excludedClasses = setOf("com.example.*Application")
    threads = 4
    outputFormats = setOf("HTML", "XML")
    timestampedReports = false
    mutationThreshold = 80
}
./gradlew pitest
# レポート:build/reports/pitest/index.html
項目 gradle-pitest-plugin 1.19.0
タスク名 pitest
レポートの場所 build/reports/pitest
targetClasses の既定 "${project.group}.*"
時間切れの定数 timeoutConstInMillis(Maven の timeoutConstant に当たる)
前回の結果を使う historyInputLocation・historyOutputLocation

README では、Gradle 8.1 以降は Kotlin DSL でも = で書けるとしています(それより前は .set(...))。Groovy DSL との対訳は、関連記事「build.gradle書き方|Groovy/Kotlin DSL対訳と依存設定一覧」で扱っています。

コマンドラインで動かす

ビルドツールを使わずに動かすこともできます。この記事の結果は、この方法で出したものです。

java -cp "pitest-command-line.jar:pitest-entry.jar:pitest.jar:pitest-html-report.jar:(依存のjar):build/main:build/test:(テストのjar)" \
  org.pitest.mutationtest.commandline.MutationCoverageReport \
  --reportDir build/pit-reports \
  --targetClasses 'com.example.*' \
  --excludedClasses 'com.example.*Test' \
  --targetTests 'com.example.*Test' \
  --sourceDirs src/main/java \
  --outputFormats HTML,XML,CSV \
  --timestampedReports=false

--targetClasses 'com.example.*' だけだと、テストクラス自体もミューテーションの対象になりました。 サンプルでは、テストクラスが入ると 13 個のミュータントができ、そのうちテスト側の VoidMethodCallMutator が4個生き残りました。テストクラスとテストが同じパッケージにあるときは、--excludedClasses で外します。

実行時間の注意

ミューテーションテストは、ミュータントの数だけテストを流すので、通常のテストより時間がかかります。PIT の Timings の表示で、どこに時間がかかったかが分かります。

- Timings
> pre-scan for mutations : < 1 second
> scan classpath : < 1 second
> coverage and dependency analysis : < 1 second
> build mutation tests : < 1 second
> run mutation analysis : 1 seconds
> Total  : 2 seconds
時間を抑える方法 設定
並列に流す threads(既定 1)
対象を絞る targetClasses・excludedClasses・excludedMethods・avoidCallsTo(ログ出力など)
遅いテストを外す targetTests・excludedTestClasses・excludedGroups(結合テストを外す)
2回目以降を速くする Maven withHistory、Gradle historyInputLocation・historyOutputLocation
CI で毎回流さない 夜間・週1のジョブにする、プルリクエストでは変更したモジュールだけ

生き残るミュータントほど時間がかかります。 KILLED は失敗したテストで止まりますが、SURVIVED はその行を通るテストを全部流すからです。500 ミリ秒かかるテストを1つ足して比べると、直した後のテストでは流したテストは合計 15 回のまま、直す前のテストでは 16 回に増えました。

時間切れ(TIMED_OUT)は、通常の実行時間 × timeoutFactor + timeoutConstant を超えたときです。 ループの条件を書き換えると無限ループになることがあり、そのときに使われます。時間切れは検出扱いです。

Python の mutmut と比べる

Python では mutmut がよく使われます。同じ「送料」の関数で試しました。

# src/cart/__init__.py
def shipping_fee(subtotal: int) -> int:
    """小計 5000 円以上なら送料無料、それ以外は 500 円"""
    if subtotal >= 5000:
        return 0
    return 500
# pyproject.toml
[tool.mutmut]
source_paths = ["src/"]
pytest_add_cli_args_test_selection = ["tests/"]

[tool.pytest.ini_options]
pythonpath = ["src"]
pip install mutmut pytest
mutmut run
mutmut results
mutmut show cart.x_shipping_fee__mutmut_1

テストが 6000 と 1000 だけのときの結果です。

⠧ 4/4  🎉 2 🫥 0  ⏰ 0  🤔 0  🙁 2  🔇 0  🧙 0
    cart.x_shipping_fee__mutmut_1: survived
    cart.x_shipping_fee__mutmut_2: survived
-    if subtotal >= 5000:
+    if subtotal > 5000:
-    if subtotal >= 5000:
+    if subtotal >= 5001:

PIT と同じく、境界の書き換えが生き残りました。 mutmut は数値を 1 ずらす書き換え(5000 → 5001)も作ります。境界値のテスト(5000 と 4999)を足すと、4個すべてが killed になりました。

比べる点 PIT(Java) mutmut(Python)
書き換える対象 バイトコード ソースのコピー(mutants フォルダ)
設定の場所 pom.xml・build.gradle.kts pyproject.toml の [tool.mutmut]
結果の見方 HTML・XML・CSV mutmut results・mutmut show・mutmut browse
状態の表示 KILLED・SURVIVED など 🎉 killed・🙁 survived・🫥 no tests・⏰ timeout など
3.8.0 での設定名の変更 — paths_to_mutate → source_paths(以前の名前は警告)

mutmut 3.8.0 で paths_to_mutate・tests_dir を使うと、The config paths_to_mutate is deprecated. Please rename it to source_paths という警告が出ました。pytest の使い方は、関連記事「pytest 使い方|導入からfixture・mock・カバレッジまで実行結果つき」で扱っています。

導入の進め方

段階 やること
1. 小さく試す 業務ロジックのパッケージ1つだけを targetClasses にする
2. 生き残りを読む SURVIVED の行を見て、足りない境界値・判定を足す
3. 意味のない生き残りを外す ログ出力は avoidCallsTo、getter・自動生成は excludedMethods
4. 基準を決める 今の値を少し下回る mutationThreshold から始める
5. CI に組み込む 夜間ジョブや、変更したモジュールだけで流す

生き残ったミュータントが、必ずしもテストの不足とは限りません。 書き換えても振る舞いが変わらない「等価ミュータント」(例:ループの終了条件 i < n を i != n にしても結果が同じ)は、どんなテストでも検出できません。生き残りを1つずつ読み、テストを足すか、除外するかを決めます。

よくある質問

Q. No mutations found でエラーになります。 A. targetClasses のパターンが、対象のクラスに合っていないことが多いです。ソースの既定では failWhenNoMutations が true のため、ミュータントが0個のときに失敗します。パッケージ名と * の位置を確かめます。

Q. JUnit 5 のテストが流れません。 A. pitest-junit5-plugin を PIT のプラグインの依存に足します(Maven は <plugin> の中の <dependencies>、Gradle は junit5PluginVersion)。JUnit の書き方は、関連記事「JUnitの使い方|単体テストの書き方と観点【JUnit 5・6】」で扱っています。

Q. モックを使ったテストでも効きますか。 A. 効きます。サンプルの audit.record(...) のように、呼び出しを消すミュータントが生き残ったときは、モックで呼び出しを確かめる(verify)テストを足します。Mockito の使い方は、関連記事「Mockitoの使い方|JUnit 5での基本からよくあるエラーまで」で扱っています。

Q. ミューテーションスコアは何%を目指せばよいですか。 A. 決まった値はありません。全体に一律の基準を置くより、業務ロジックのパッケージに絞って測り、今の値を下回らないようにする使い方が始めやすいです。

まとめ

  • ミューテーションテストは、わざと入れたバグをテストが検出できるかを測る
  • サンプルでは、行カバレッジ 100% でも 2 個のミュータントが生き残った(境界値の不足と、ログの確認の不足)
  • テストを2件足すと、カバレッジは 100% のまま、ミューテーションスコアが 78% → 100% に上がった
  • 既定のミューテーターは 11 個。DEFAULTS を明示すると、1.30.0 では内訳が少し違う
  • Maven は pitest-maven の mutationCoverage、Gradle は info.solidsoft.pitest の pitest タスク。JUnit 5 は pitest-junit5-plugin を足す
  • 時間は 対象を絞る・threads・withHistory で抑え、CI では夜間や変更分だけで流す

参考・出典

確認日はいずれも 2026年10月13日です。

  • PIT 公式サイト:https://pitest.org/
  • PIT「Mutation Operators」:https://pitest.org/quickstart/mutators/
  • PIT「Maven quick start」:https://pitest.org/quickstart/maven/
  • PIT「Command Line quick start」:https://pitest.org/quickstart/commandline/
  • PIT(GitHub、ソース・タグ 1.30.0):https://github.com/hcoles/pitest
  • pitest-junit5-plugin(GitHub、タグ 1.2.3):https://github.com/pitest/pitest-junit5-plugin
  • gradle-pitest-plugin(GitHub、1.19.0):https://github.com/szpak/gradle-pitest-plugin
  • mutmut(GitHub):https://github.com/boxed/mutmut

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ミューテーションテストの始め方|PIT(Java)の設定と結果の読み方」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?