---
title: "CDI-Unitとは？Java CDIのBeanをJUnit 5でテストする設定とweld-junitとの選び方"
url: "https://www.issoh.co.jp/tech/details/6490/"
published: 2025-05-01
updated: 2026-09-15
categories: ["自動化"]
publisher: "株式会社一創"
---

# CDI-Unitとは？Java CDIのBeanをJUnit 5でテストする設定とweld-junitとの選び方

CDI-Unitは、テストのたびにWeld SEを起動し、Java CDI（Jakarta Contexts and Dependency Injection）のBeanへ本番と同じ仕組みで注入させる単体テスト用ライブラリです。アプリケーションサーバーを立てずに、@Injectやalternative、スコープを含めた動作を確かめられます。なお「CDI」は業種によって別の略語にも使われますが、本記事ではJavaの依存性注入仕様を指します。

2026年9月15日時点で、公開されている最新版は安定版の4.4.0（javax向け）と、jakarta向けの5.0.0-EA7（Early Access）です。どちらを入れるか、そもそもCDI-Unitを選ぶべきかで判断が分かれるため、版の違い・Maven設定・JUnit 5での書き方を実行結果とあわせて整理します。

## まとめ：CDI-Unitの版の選び方と使いどころ

- **javax名前空間（CDI 1.x／2.x）のプロジェクト**は、安定版の`io.github.cdi-unit:cdi-unit:4.4.0`を使います。4系は機能追加もバックポートもされません。
- **jakarta名前空間（CDI 3.x以降）**に対応するのは5系で、公開済みの最新Early Access版は5.0.0-EA7です。2024年9月29日の公開から約2年、Final版は出ていません。
- CDI-UnitはWeldを依存に含めません。`weld-se-core`を自分で追加しないと起動しません。
- JUnit 5では`@ExtendWith(CdiJUnit5Extension.class)`を付けます。後述の例では、自動検出されない実装クラスを`@AdditionalClasses`で追加し、指定しないと`WELD-001408`で落ちます。
- 新規のjakartaプロジェクトなら、Final版があるweld-junit（weld-junit5 5.0.3.Final）を先に検討します。実サーバー上のJPAやトランザクションまで確かめたい結合テストはArquillianの領分です。

## Java CDIとCDI-Unitの関係

CDIはJakarta EEの仕様の一つで、Beanの生成・注入・スコープ管理・イベント・インターセプターの振る舞いを定めています。仕様の現行版は**Jakarta EE 11に含まれるCDI 4.1**で、Jakarta EE 12向けのCDI 5.0は開発中です（jakarta.eeの仕様ページ）。仕様を実装するコンテナの一つがWeldで、Java SE上で起動できる形態がWeld SEです。

Springのコンテナも依存性注入を行いますが、CDIとは別系統の仕組みで、アノテーションや設定の流儀が異なります。Spring側の注入方式は[SpringのDI（依存性注入）とは？仕組みと3つの注入方法をわかりやすく解説](/tech/details/2303/)で扱っています。

CDIのBeanをMockitoだけでテストすると、フィールド注入やalternativeの選択、@RequestScopedのようなスコープはテストコード側で再現するしかありません。CDI-Unitは、テストクラスを起点に必要なクラスをWeld SEへデプロイし、テストクラス自身にも@Injectで注入します。コンテナが解決するので、注入漏れや曖昧な依存はテストの時点でデプロイエラーとして現れます。

## CDI-Unit 4.4.0と5.0.0-EA7の対応範囲

| 項目        | 4.4.0                 | 5.0.0-EA7                    |
| --------- | --------------------- | ---------------------------- |
| 公開日       | 2024-07-07            | 2024-09-29                   |
| 位置付け      | 安定版                   | Early Access                 |
| 名前空間      | javax（CDI 1.x／2.x）    | jakarta（CDI 3.x以降）           |
| Java      | 8／11                  | 11以上                         |
| POM上のWeld | 3.1.9.Final（provided） | 4.0.3.Final（provided）        |
| テストランナー   | JUnit 4／TestNG        | JUnit 4／JUnit 5／TestNG／Spock |

[CDI-UnitのGitHubリポジトリ](https://github.com/cdi-unit/cdi-unit)のREADMEには「一度にサポートするメジャー版は一つだけで、変更は旧版へバックポートしない」と明記されています。GitHubのmasterブランチは5.0.0-SNAPSHOTのまま2026年9月も依存ライブラリの更新が続いており、開発は5系で進んでいます。

### groupIdとパッケージ名の移行

4.3でgroupIdが`org.jglue.cdi-unit`から`io.github.cdi-unit`へ移りました。Maven Centralのjarを確認すると、パッケージも4.2.0までが`org.jglue.cdiunit`、4.3.0以降が`io.github.cdiunit`です。旧座標の扱いは版によって違います。

- `org.jglue.cdi-unit:cdi-unit:5.0.0-EA7`はio.github側へ転送するrelocation POMです。
- `org.jglue.cdi-unit:cdi-unit:4.4.0`はpackagingがpomで、jarが置かれていません。4.4.0は必ず`io.github.cdi-unit`で指定します。

4.2.0以前から上げる場合は、依存の座標に加えてimport文の`org.jglue.cdiunit`も書き換える必要があります。

### 5.0.0-EA7で増えたテストランナー

5.0.0-EA7のリリースノートには、JUnit 5対応（Nestedテストを含む）、TestNG・Spock対応のモジュール分割、Jakarta EE対応、Weld 5.1.3.Final対応が並んでいます。集約アーティファクト`cdi-unit`を指定すると`cdi-unit-junit5`や`cdi-unit-testng`もまとめて入ります。JUnit 5用の拡張は`io.github.cdiunit.junit5.CdiJUnit5Extension`です。

## Mavenの依存関係設定

検証用プロジェクトでは、次の依存関係に加えて`maven.compiler.release`を17、`maven-surefire-plugin`を3.5.2に指定し、`mvn test`で後述のテストを実行しました。CDI-Unit本体のPOMではWeldとMockitoがprovidedスコープなので、テスト側で明示します。

```
<dependency>
  <groupId>jakarta.enterprise</groupId>
  <artifactId>jakarta.enterprise.cdi-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>io.github.cdi-unit</groupId>
  <artifactId>cdi-unit</artifactId>
  <version>5.0.0-EA7</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.jboss.weld.se</groupId>
  <artifactId>weld-se-core</artifactId>
  <version>5.1.3.Final</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>5.11.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>5.23.0</version>
  <scope>test</scope>
</dependency>
```

javaxのプロジェクトで4.4.0を使う場合は、座標を`io.github.cdi-unit:cdi-unit:4.4.0`にし、Weld SEも3.x系（4.4.0のPOMは3.1.9.Final）を合わせます。JUnitは4系です。Gradleでも座標は同じです。CDI APIは本番コードのコンパイル用に`compileOnly`へ、テスト用にも`testImplementation`へ追加し、CDI-Unit・Weld・JUnit・Mockitoは`testImplementation`へ指定します。

## JUnit 5でのテストの書き方

ここからの例は、`Engine`インターフェースを@Injectで受け取る`Starship`クラスと、その実装`WarpDrive`を題材にしています（CDI-Unit公式READMEの例と同じ構成）。以下の3型はそれぞれEngine.java、WarpDrive.java、Starship.javaに保存し、Starship.javaには`import jakarta.inject.Inject;`を追加します。各型と後述のテストは同じパッケージに配置します。

```
public interface Engine {
    String start();
}

public class WarpDrive implements Engine {
    @Override
    public String start() {
        return "warp";
    }
}

public class Starship {
    @Inject
    Engine engine;

    public String start() {
        return engine.start();
    }
}
```

### @AdditionalClassesによる実装クラスの追加

CDI-Unitがデプロイするのは、テストクラスから辿れるクラスが中心です。インターフェースの実装はテストクラスから見えないため、`@AdditionalClasses`で明示します。

```
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.github.cdiunit.AdditionalClasses;
import io.github.cdiunit.junit5.CdiJUnit5Extension;

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

@ExtendWith(CdiJUnit5Extension.class)
@AdditionalClasses(WarpDrive.class)
class StarshipTest {

    @Inject
    Starship starship;

    @Test
    void startsWithWarpDrive() {
        assertEquals("warp", starship.start());
    }
}
```

このテストは成功します。`@AdditionalClasses`の行だけを外すと、テストインスタンスの生成時点で次のエラーになりました。

```
WELD-001408: Unsatisfied dependencies for type Engine with qualifiers @Default
```

この例は、インターフェースの実装を自動検出できない場合の失敗を示しています。`WELD-001408`が出たら、まず注入先の型の実装がデプロイに含まれているかを疑います。同じパッケージのクラスをまとめて入れるなら`@AdditionalPackages`が使えます。

### @Produces・@Mock・@ProducesAlternativeによるモックへの差し替え

テストクラスのフィールドに`@Produces`とMockitoの`@Mock`を併記すると、そのモックがCDIのBeanとして供給されます。本物の実装も同時にデプロイされている場合は、`@ProducesAlternative`を付けてモックを優先させます。

```
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import io.github.cdiunit.AdditionalClasses;
import io.github.cdiunit.ProducesAlternative;
import io.github.cdiunit.junit5.CdiJUnit5Extension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(CdiJUnit5Extension.class)
@AdditionalClasses(WarpDrive.class)
class StarshipMockTest {

    @Produces
    @ProducesAlternative
    @Mock
    Engine engine;

    @Inject
    Starship starship;

    @Test
    void mockReplacesWarpDrive() {
        when(engine.start()).thenReturn("mock");
        assertEquals("mock", starship.start());
        verify(engine).start();
    }
}
```

`WarpDrive`がデプロイされていても、`Starship`にはモックが注入され、`"mock"`が返ります。モックとスタブの使い分けそのものは[モックとは？スタブとの違い・テストダブル5分類と単体テストでの使い分けを実装者向けに解説](/tech/details/13505/)を参照してください。

### JUnit 4とTestNGでのランナー指定

JUnit 4では`@RunWith(CdiRunner.class)`を付けます。`CdiRunner`の完全修飾名は版で異なり、4.2.0までは`org.jglue.cdiunit.CdiRunner`、4.3.0以降は`io.github.cdiunit.CdiRunner`です。5.0.0-EA7ではモジュール分割に合わせて`io.github.cdiunit.junit4.CdiRunner`も追加されています。

TestNGは版で方式が違います。4.4.0のjarにあるのは継承して使う`NgCdiRunner`だけで、現行の公式ガイドが示す`@Listeners(NgCdiListener.class)`は5.0.0-EA7のjarで提供されています。JUnit自体の基本は[JUnitとは？Javaの単体テストフレームワークの基本と書き方【2026年版】](/tech/details/3064/)で扱っています。

### デプロイとスコープを制御するアノテーション

| アノテーション                | 公式ガイドでの役割                    |
| ---------------------- | ---------------------------- |
| @AdditionalClasses     | 指定クラスをデプロイに追加                |
| @AdditionalPackages    | 指定クラスと同じパッケージを追加             |
| @AdditionalClasspaths  | 同じクラスパスエントリのクラスを追加           |
| @IgnoredClasses        | 指定クラスをデプロイから除外               |
| @ActivatedAlternatives | alternativeを検出対象にして有効化       |
| @ProducesAlternative   | モックを他の実装より優先                 |
| @InRequestScope        | テストメソッド中にrequestスコープを開始      |
| @InSessionScope        | テストメソッド中にsessionスコープを開始      |
| @InConversationScope   | テストメソッド中にconversationスコープを開始 |

@RequestScopedや@SessionScopedのBeanを使うテストでは、テストクラスかメソッドに`@InRequestScope`などを付けてスコープを開始します。EJBを注入する`@SupportEjb`、JAX-RSのクラスを使える`@SupportJaxRs`も用意されています。

## 実行して分かったCDI-Unit 5.0.0-EA7の注意点

2026年9月15日に、macOS上のOpenJDK 26.0.2とMaven 3.9.16で前述のテストを実行しました。公式ドキュメントからは読み取れない差が2つ出ています。

| 組み合わせ                              | 結果          |
| ---------------------------------- | ----------- |
| Weld SE 5.1.3.Final＋Mockito 5.14.0 | モックのテストだけ失敗 |
| Weld SE 5.1.3.Final＋Mockito 5.23.0 | 全テスト成功      |
| Weld SE 6.0.3.Final＋Mockito 5.23.0 | 全テスト成功      |

### Mockito 5.14.0とJDK 26の組み合わせでの初期化失敗

5.0.0-EA7のPOMが参照しているMockitoは5.14.0です。これをそのまま使うと、JDK 26では`Could not initialize plugin: interface org.mockito.plugins.MockMaker`に続いて`Unknown Java version: 0`が出てモックを作れませんでした。記録した環境ではMockitoを5.23.0に上げて成功しました。ただし、Java 21以降ではエージェントの動的接続が制限される場合があります。接続に失敗する環境では、テストJVMの`-javaagent`引数にMockitoのjarを指定します。CDI-Unit側ではなく、テストを動かすJDKにMockitoの版を合わせる必要があります。

### Weld SE 6.0.3.Finalでの注入・モック差し替えの検証結果

リリースノートで明示されている対応はWeld 5.1.3.Finalまでですが、Weld SE 6.0.3.Finalに差し替えても上記2本のテストは成功しました。ただし確認したのは注入とモック差し替えだけです。スコープやインターセプターを多用するテストでは、Weld 6.0へ上げる前に既存テストを一通り流して判断します。

## weld-junit・Arquillianとの選び分け

CDIのBeanをテストする道具はCDI-Unitだけではありません。Weldプロジェクト自身がweld-testing（JUnit 4／JUnit 5／Spock向け拡張）を公開しており、統合テストの側にはArquillianがあります。

| 項目     | CDI-Unit              | weld-junit5       | Arquillian   |
| ------ | --------------------- | ----------------- | ------------ |
| 最新版    | 4.4.0／5.0.0-EA7       | 5.0.3.Final       | 1.10.2.Final |
| 公開日    | 2024-07-07／2024-09-29 | 2025-10-08        | 2026-05-20   |
| 対象     | javax／jakarta         | Weld 6.0（CDI 4.1） | 実コンテナ        |
| 起動するもの | Weld SE               | Weld SE           | コンテナへデプロイ    |
| 主な用途   | 単体テスト                 | 単体テスト             | 統合テスト        |

weld-junit5では、`@EnableWeld`を付けて`WeldInitiator`でデプロイするクラスを指定します。前述の`Starship`をモック注入でテストすると次の形になり、weld-junit5 5.0.3.Final（Weld 6.0.3.Final）で成功しました。

```
import jakarta.inject.Inject;
import org.jboss.weld.junit.MockBean;
import org.jboss.weld.junit5.EnableWeld;
import org.jboss.weld.junit5.WeldInitiator;
import org.jboss.weld.junit5.WeldSetup;
import org.junit.jupiter.api.Test;

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

@EnableWeld
class StarshipWeldTest {

    @WeldSetup
    WeldInitiator weld = WeldInitiator.from(Starship.class)
            .addBeans(MockBean.builder()
                    .types(Engine.class)
                    .creating((Engine) () -> "mock")
                    .build())
            .build();

    @Inject
    Starship starship;

    @Test
    void mockBeanIsInjected() {
        assertEquals("mock", starship.start());
    }
}
```

weld-junit5のREADMEが挙げる要件はJUnit 5とJava 17です。2026年6月12日公開の6.0.0.Beta1ではWeld 7.0（CDI 5.0）対応に合わせてアーティファクト名が`weld-junit-jupiter`に、パッケージが`org.jboss.weld.junit.jupiter`に変わりました。移行用にOpenRewriteのレシピが提供されています。

選び方は次のとおりです。

- **既存のjavaxプロジェクトでCDI-Unit 4系のテストが大量にある**：4.4.0に揃えて維持します。jakartaへ移行するときに、`@AdditionalClasses`などのアノテーション体系をそのまま持ち込めるのが5.0.0-EA7の価値です。
- **新規のjakartaプロジェクト**：weld-junit5 5.0.3.Finalを選びます。CDI-Unit 5はFinal版が無く、CIの基盤に据えるには版の固定と上げ時の検証を自分で抱えることになります。
- **JPA・JTA・EJBなど実サーバーの機能ごと確かめたい**：Arquillianでコンテナへデプロイします。前述の実行でもWeld SEは起動時に`WELD-000101: Transactional services not available`を出しており、トランザクションに依存する振る舞いはCDI-Unitの守備範囲外です。

単体テストと統合テストの本数配分は、[テストピラミッドとは？単体・結合・E2Eの配分と実装者向けテスト戦略設計](/tech/details/13289/)の考え方で決めると、CDI-UnitとArquillianの役割が重なりません。

## よくある質問

### JavaのCDIとは何ですか？

Jakarta EEの仕様であるJakarta Contexts and Dependency Injectionの略で、Beanの注入・スコープ・イベントなどを定めています。現行版はJakarta EE 11に含まれるCDI 4.1で、実装にはWeldなどがあります。

### CDI-UnitはJUnit 5で使えますか？

5.0.0-EA7で使えます。テストクラスに`@ExtendWith(io.github.cdiunit.junit5.CdiJUnit5Extension.class)`を付けます。安定版の4.4.0はJUnit 4とTestNGのみです。

### CDI-Unitを入れればWeldも自動で入りますか？

入りません。CDI-UnitのPOMではWeldがprovidedスコープのため、`org.jboss.weld.se:weld-se-core`をテストスコープで追加します。4.4.0なら3.x系、5.0.0-EA7ならWeld 5.1.3.Finalが公式に対応を明記している版です。

### org.jglue.cdi-unitとio.github.cdi-unitはどちらを指定しますか？

io.github.cdi-unitを指定します。4.3でgroupIdが移っており、org.jglue側の4.4.0にはjarがありません。4.2.0以前から上げる場合はimport文の`org.jglue.cdiunit`も`io.github.cdiunit`へ書き換えます。

### WELD-001408が出たときはどこを見ればよいですか？

注入先の型と修飾子（qualifier）を満たす有効なBeanが見つからない状態です。実装の追加漏れに加え、修飾子の不一致やalternativeの有効化漏れも確認します。インターフェースの実装クラスを`@AdditionalClasses`で追加するか、テスト側で`@Produces`付きのフィールドやメソッドから供給します。

## 関連記事

- [JUnitとは？Javaの単体テストフレームワークの基本と書き方【2026年版】](/tech/details/3064/)
- [モックとは？スタブとの違い・テストダブル5分類と単体テストでの使い分けを実装者向けに解説](/tech/details/13505/)
- [テストピラミッドとは？単体・結合・E2Eの配分と実装者向けテスト戦略設計](/tech/details/13289/)
- [Testcontainersとは？Dockerで作る使い捨てテスト環境と2.0系の移行点](/tech/details/4629/)
- [PowerMockitoとは？静的・privateメソッドのMock化とMockitoネイティブへの移行判断](/tech/details/2351/)

---

出典: [CDI-Unitとは？Java CDIのBeanをJUnit 5でテストする設定とweld-junitとの選び方](<https://www.issoh.co.jp/tech/details/6490/>)（株式会社一創）
