1. はじめに

本チュートリアルでは、Java製の「ダイスゲーム」を題材に、JUnit や assert を使わずに System.out.println 出力だけでテストを行う手法を学びます。IDE には Pleiades を使用し、手軽なコンソール確認ベースのテスト手順を身につけることを目指します。

今回の単体テスト、結合テスト、システムテストの範囲は以下のとおりです。

新人エンジニア研修
今回のテスト範囲

対象読者

  • Java の基本文法やクラス設計は理解しているが、テストの経験が浅い方
  • JUnit などのフレームワークをまだ導入しておらず、手動・目視でのテストからスタートしたい方
  • Pleiades(Eclipse ベース)で開発を行っている新人エンジニア

なぜ JUnit/assert を使わずに学ぶのか

  • テストコードの最初の一歩として、まずは「動作確認」の流れを理解する
  • 外部ライブラリや依存を増やさずに、「自作コードを素のまま検証する」感覚を養う
  • 後にフレームワーク導入した際の差分やメリットがより実感しやすくなる

Pleiades IDE でのテストの流れ

  1. Java プロジェクトにテスト用クラス(メインメソッド)を追加
  2. 対象クラス(Dice, DiceGame)を呼び出し、System.out.println で結果を出力
  3. コンソールビューを開いてログを目視確認
  4. 問題があればソースを修正し、再度実行

テスト対象コード

以下のコードをテスト対象とします。

package diceGame;

import java.util.Random;

/**
 * Dice クラスは、6面のサイコロを表現するクラスです。 インスタンス化するとサイコロを1つ生成し、振ることで1〜6の目をランダムに得ることができます。
 * 各目は対応する絵文字(⚀〜⚅)で表現されます。
 * 
 * @author Yusei Yamazaki
 * @version 1.0.0
 */
public class Dice {

	/**
	 * サイコロの現在の目の値を表します。初期値は1です。
	 */
	private int faceValue = 1;

	/**
	 * 各サイコロの目に対応する絵文字を格納する配列です。 インデックス0〜5にそれぞれ⚀〜⚅が格納されています。
	 */
	private char[] faces = { '⚀', '⚁', '⚂', '⚃', '⚄', '⚅' };

	/**
	 * 乱数生成器。サイコロの目をランダムに決めるために使用されます。
	 */
	private Random r;

	/**
	 * コンストラクタ。乱数生成器のインスタンスを初期化します。
	 */
	public Dice() {
		r = new Random();
	}

	/**
	 * サイコロを振って、1〜6のいずれかの目の値をランダムに設定します。
	 */
	public void roll() {
		faceValue = r.nextInt(6) + 1; // 0〜5に1を加えることで1〜6の値にする
	}

	/**
	 * 現在のサイコロの目の値(数値)を取得します。
	 *
	 * @return 現在のサイコロの目の値(1〜6)
	 */
	public int getFaceValue() {
		return faceValue;
	}

	/**
	 * 現在のサイコロの目を対応する絵文字で取得します。
	 *
	 * @return faceValue に対応する絵文字(⚀〜⚅)
	 */
	public char getFace() {
		return faces[faceValue - 1]; // faceValueは1始まりなので、インデックスに合わせて-1
	}
}

package diceGame;

import java.util.Scanner;

/**
 * DiceGame クラスは、2つのサイコロを使ってプレイヤーが無料または有料になるかを判定する簡単なゲームを提供します。 サイコロの合計が 5〜8
 * であれば「無料」、それ以外は「有料」となります。 サイコロの目が1だった場合は赤色で表示されます。
 * 
 * @author Yusei Yamazaki
 * @version 1.0.0
 */
public class DiceGame {

	// ANSI エスケープコード(赤色)の定義。数字「1」の目を強調表示するために使う。
	public static final String ANSI_RED = "\u001B[31m";
	public static final String ANSI_RESET = "\u001B[0m";

	/**
	 * 1つ目のサイコロオブジェクト。
	 */
	Dice d1;

	/**
	 * 2つ目のサイコロオブジェクト。
	 */
	Dice d2;

	/**
	 * DiceGame のコンストラクタ。 ゲーム開始時に2つのサイコロを生成する。
	 */
	public DiceGame() {
		this.d1 = new Dice();
		this.d2 = new Dice();
	}

	/**
	 * ゲームのメイン処理。ユーザーにダイスを振らせ、合計値により無料か有料かを判定する。 ユーザーが Enter キーを押すことでゲームが繰り返される。
	 */
	public void play() {
		while (true) {
			System.out.println("2つのサイコロの合計が7~11なら無料になるよ。Enterを押してサイコロを振ってください。");

			// ユーザーの入力待ち(Enter を押すと次に進む)
			Scanner sc = new Scanner(System.in);
			sc.nextLine();

			// 1つ目のサイコロを振る
			d1.roll();
			int num1 = d1.getFaceValue();

			// 2つ目のサイコロを振る
			d2.roll();
			int num2 = d2.getFaceValue();

			// サイコロ1の目が1だった場合は赤色で表示
			if (num1 == 1) {
				System.out.print(ANSI_RED + d1.getFace() + ANSI_RESET);
			} else {
				System.out.print(d1.getFace());
			}

			// サイコロ2の目が1だった場合は赤色で表示
			if (num2 == 1) {
				System.out.println(ANSI_RED + d2.getFace() + ANSI_RESET);
			} else {
				System.out.println(d2.getFace());
			}

			// サイコロの合計値を出力
			int point = num1 + num2;
			System.out.println("point:" + point);

			// 合計値に応じたメッセージを出力
			System.out.println(judge(point));
		}
	}

	/**
	 * サイコロの合計値によって「無料」か「有料」かを判定する。
	 *
	 * @param point 2つのサイコロの合計値
	 * @return 判定結果のメッセージ
	 */
	String judge(int point) {
		if (point >= 7 && point <= 11) {
			return "やったー、無料です。";
		} else {
			return "ざんねん、有料です。次はきっと無料だよ。";
		}
	}
}

package diceGame;

/**
 * Main クラスは、ダイスゲームを起動するエントリポイントです。 実行すると DiceGame インスタンスを生成し、ゲームを開始します。
 * 
 * @author Yusei Yamazaki
 * @version 1.0.0
 */
public class Main {

	/**
	 * アプリケーションのメインメソッドです。 DiceGame のインスタンスを生成し、ゲームループを開始します。
	 *
	 * @param args コマンドライン引数(未使用)
	 */
	public static void main(String[] args) {
		// ゲームを起動
		new DiceGame().play();
	}
}

2. プロジェクトのセットアップ

Pleiades IDE での新規 Java プロジェクト作成

  • Pleiades(Eclipse)を起動し、メニューから ファイル > 新規 > Java プロジェクト を選択
  • プロジェクト名を入力(例:DiceGameTestTutorial)し、完了 をクリック
  • プロジェクト内にデフォルトで作成される src フォルダを使用

パッケージ構成例

  • diceGame パッケージに本体クラスを、test パッケージに各テストクラスを配置
  • テストクラスはすべて public static void main(String[] args) をもつ普通の Java アプリとして実装

テスト実行の準備

  • 特別なライブラリやユーティリティは不要。System.out.println 出力をそのままコンソールで目視確認
  • 各テストクラスを右クリック → 実行 > Java アプリケーション で簡単に起動

3. 単体テスト(Unit Test)

テスト対象クラス:Dice

Dice クラスは内部で Random を使っているため、完全な自動検証は難しいですが、 手動で複数回ロールを行い、出力が 1〜6 の範囲かつ絵文字が対応しているかを目視確認します。

テストコード例:TestDice.java

package diceGame;

/**
 * Dice クラスの単体テスト。 roll() を複数回実行し、出力を目視で確認する。
 */
public class TestDice {
	public static void main(String[] args) {
		Dice dice = new Dice();
		// サンプルとして10回振ってみる
		for (int i = 1; i <= 10; i++) {
			dice.roll();
			int value = dice.getFaceValue();
			char face = dice.getFace();
			System.out.println("roll " + i + " → value=" + value + ", face=" + face);
		}
	}
}

確認ポイント

  • 出力される value が必ず 1〜6 の整数であること
  • face のいずれかであること
  • 同じインスタンスで連続実行しても、値が偏りすぎていないこと(ランダム性の目視確認)

単体テスト報告書の例

テスト実施日:20XX年5月6日

作成者:山崎有生

対象モジュール:diceGame.Dice

テスト概要:
roll(), getFaceValue(), getFace() メソッドが仕様どおり動作し、出目が 1~6 の範囲かつ絵文字が対応していることを目視で確認する。

1. テスト環境

  • OS:Windows 11
  • Java:JDK 17
  • IDE:Pleiades(Eclipse 20XX-03)

2. テスト項目

IDテスト内容期待結果備考
UT-01roll() を呼び出し、getFaceValue() が 1~6 を返す常に 1~6 の整数10 回連続実行
UT-02getFace() が faceValue に対応する絵文字を返す⚀~⚅ のいずれか各出目で確認
UT-03連続実行時に例外が発生しない例外なし

3. テスト手順

  1. Pleiades 上で TestDice.java を実行
  2. コンソールに出力された値と絵文字を目視で確認

4. 実施結果

// 実行サンプル(抜粋)
roll 1 → value=3, face=⚂
roll 2 → value=1, face=⚀
…
roll 10 → value=6, face=⚅
  • すべての value が 1~6 の範囲に収まっている
  • 対応する絵文字が正しく表示されている
  • 例外は一切発生せず

5. 合否判定

全項目合格(PASS)

6. 課題・改善点

  • 目視確認のみのテストのため、自動化スクリプトやログ比較による検証を検討すると品質向上が見込める

テスト対象メソッド:DiceGame.judge(int point)

DiceGame クラスの判定ロジック(合計値が 7~11 なら「無料」、それ以外は「有料」)を、 Dice を使わず固定のポイント値で呼び出し、期待どおりのメッセージが返るかを確認します。(限界値分析)

テストコード例:TestJudge.java

package diceGame;

/**
 * DiceGame の judge() メソッドを単体テストするクラス。 固定ポイントを渡して、出力メッセージを目視で確認する。
 */
public class TestJudge {
	public static void main(String[] args) {
		DiceGame game = new DiceGame();
		int[] testPoints = { 2, 6, 7, 11, 12 };
		for (int p : testPoints) {
			String result = game.judge(p);
			System.out.println("point=" + p + " → " + result);
		}
	}
}

確認ポイント

  • point=2および point=6および point=12のときは「ざんねん、有料です。次はきっと無料だよ。」が出力される
  • point=7 および point=11 のときは「やったー、無料です。」が出力される
  • メッセージ文言の誤字脱字がないこと

単体テスト報告書の例

テスト実施日:20xx年5月6日

作成者:山崎有生

対象モジュール:diceGame.DiceGame.judge(int point)

テスト概要:
固定のポイント値(2, 6, 7, 11, 12)を渡し、「無料/有料」判定メッセージが仕様どおり返るかを確認する。

1. テスト環境

  • OS:Windows 11
  • Java:JDK 17
  • IDE:Pleiades(Eclipse 20XX-03)

2. テスト項目

IDテスト内容期待結果備考
UT-01point=2 を渡す「ざんねん、有料です。次はきっと無料だよ。」下限未満ケース
UT-02point=6 を渡す「ざんねん、有料です。次はきっと無料だよ。」下限境界値ケース
UT-03point=7 を渡す「やったー、無料です。」下限境界値ケース
UT-04point=11 を渡すやったー、無料です。」上限境界値ケース
UT-05point=12 を渡す「ざんねん、有料です。次はきっと無料だよ。」
上限境界値ケース
UT-06連続実行時に例外が発生しない例外なし

3. テスト手順

  1. Pleiades 上で TestJudge.java を実行
  2. コンソールに出力された判定メッセージを目視で確認
  3. 各ポイントに対し、期待どおりのメッセージが表示されているか検証

4. 実施結果

// 実行サンプル
point=2 → ざんねん、有料です。次はきっと無料だよ。
point=6 → ざんねん、有料です。次はきっと無料だよ。
point=7 → やったー、無料です。
point=11 → やったー、無料です。
point=12 → ざんねん、有料です。次はきっと無料だよ。
  • IT-01~IT-05:すべて期待どおりのメッセージが表示された
  • IT-06:例外は発生せず、正常に終了した

5. 合否判定

全項目合格(PASS)

6. 課題・改善点

  • 判定ロジック以外の視覚要素(赤色表示など)は未テスト。必要に応じてスクリーンショット比較などを検討

4. 結合テスト(Integration Test)

Dice クラスと DiceGame クラスの連携動作を確認するために手動検証形式で、出力を目視確認した。

テスト対象

  • クラス
    • diceGame.Dice
    • diceGame.DiceGame
  • メソッド
    • Dice.roll()
    • Dice.getFaceValue()
    • DiceGame.judge(int point)

テストコード例:TestDice.java

package diceGame;

/**
 * Dice と DiceGame を連携させた結合テスト。 実際にダイスを振り、ゲームロジックを通して結果を確認する。
 */
public class TestDiceGameIntegration {
	public static void main(String[] args) {
		DiceGame game = new DiceGame();

		// テストを10回繰り返して Dice → judge の連携を確認する
		for (int i = 0; i < 10; i++) {
			// ダイスを振る
			game.d1.roll();
			game.d2.roll();

			// 出た目の値を取得
			int num1 = game.d1.getFaceValue();
			int num2 = game.d2.getFaceValue();

			// 合計を算出
			int point = num1 + num2;

			// 判定
			String result = game.judge(point);

			// 出力結果を表示(目 + 合計 + 判定)
			System.out.println("Roll " + (i + 1));
			System.out.println("Dice1: " + num1 + " (" + game.d1.getFace() + ")");
			System.out.println("Dice2: " + num2 + " (" + game.d2.getFace() + ")");
			System.out.println("Total: " + point);
			System.out.println("Result: " + result);
			System.out.println("--------------------------");
		}
	}
}

結合テスト報告書の例

テスト実施日:20XX年5月6日

作成者:山崎有生

1. 概要

本書は、Dice クラスと DiceGame クラスの連携動作を確認するために実施した結合テスト(Integration Test)の結果をまとめたものである。テストは手動検証形式で、出力を目視確認した。


2. テスト対象

  • クラス
    • diceGame.Dice
    • diceGame.DiceGame
  • メソッド
    • Dice.roll()
    • Dice.getFaceValue()
    • DiceGame.judge(int point)

3. テスト目的

Dice クラスの roll() によって生成されたランダムな目(1~6)が DiceGamejudge() メソッドに正しく渡され、期待通りの判定(「無料」または「有料」)が出力されることを確認する。


4. テスト環境

項目内容
IDEPleiades All in One(Eclipse)
JavaバージョンJava 17
実行形式public static void main(String[] args) で起動
テスト形式手動結合テスト(コンソール出力を目視確認)
テストクラス名TestDiceGameIntegrationReal

5. テスト方法

  • DiceGame オブジェクトを作成し、d1, d2roll() メソッドで乱数を取得
  • 2つのサイコロの目の合計値を judge() に渡してメッセージを取得
  • 出力結果(目・絵文字・合計・判定メッセージ)を10回繰り返し実行・目視確認


6. テスト結果(抜粋)

テスト回Dice1Dice2合計判定メッセージ
1235ざんねん、有料です。次はきっと無料だよ。
2156ざんねん、有料です。次はきっと無料だよ。
3426ざんねん、有料です。次はきっと無料だよ。
4639やったー、無料です。
5112ざんねん、有料です。次はきっと無料だよ。
...............

※ 合計値が 7~11のとき「無料」, それ以外は「有料」と判定されていることを確認。


7. 評価と結論

  • Dice クラスからのランダムな値が DiceGame に正しく渡されており、判定ロジックが正しく機能していることを確認
  • 表示内容に誤字・フォーマット崩れなし
  • 複数クラスにまたがるロジックが期待通りに動作しており、本テストは結合テストとして妥当

⇒ 結果:合格(Pass)

5. システムテスト(System Test)

目的

本テストでは、チンチロリンハイボールゲームで「無料」と判定される確率が 55%±1% になるかを 統計的に検証します。

試行回数とフロー

  • 試行回数:100,000 回
  • テストフロー:
    1. 内部の 2 つのサイコロ(game.d1, game.d2)を100,000回振る
    2. 出目の合計が 7~11 の場合を「無料」としてカウント
    3. 無料回数/全試行数から無料率を算出

テストコード例:TestDiceGameStat.java

package diceGame;

/**
 * DiceGame のシステムテスト。 100,000 回試行し、無料率が 55%±1% の範囲内かを検証する。
 */
public class TestDiceGameSystem {
	public static void main(String[] args) {
		DiceGame game = new DiceGame();
		int trials = 100_000;
		int freeCount = 0;

		for (int i = 0; i < trials; i++) {
			game.d1.roll();
			game.d2.roll();
			int sum = game.d1.getFaceValue() + game.d2.getFaceValue();
			if (sum >= 7 && sum <= 11) {
				freeCount++;
			}
		}

		double rate = freeCount * 100.0 / trials;
		System.out.println("試行回数: " + trials);
		System.out.println("無料回数: " + freeCount);
		System.out.println("無料率: " + rate + "%");
		if (rate >= 54 && rate <= 56) {
			System.out.println("※無料率は 55%±1% の範囲内です。");
		} else {
			System.out.println("※無料率は 55%±1% の範囲外です。");
		}
	}
}

確認ポイント

  • 出力される無料率が 54〜56% の範囲内に収まっているか
  • コンソールに試行回数・無料回数・無料率・判定メッセージが正しく表示されるか
  • 100,000 回の試行にかかる時間が数秒程度で収まっているか

システムテスト報告書の例

テスト実施日:20XX年5月6日

作成者:山崎有生

対象モジュール:diceGame.DiceGame(および内部 Dice クラスを含む統合動作)

テスト概要:
100,000 回の繰り返し試行を行い、「無料」判定の発生率が仕様どおり 55%±1%(54~56%)になることを統計的に検証する。

1. テスト環境

  • OS:Windows 11
  • Java:JDK 17
  • IDE:Pleiades(Eclipse 20XX-03)

2. テスト項目

IDテスト内容期待結果備考
ST-01100,000 回試行後の「無料」判定発生回数を計測無料率が 54~56% の範囲内統計的許容範囲
ST-02試行中に例外が発生しないこと例外なしで完走
ST-03試行に要する時間が許容範囲内(数秒程度)であること全試行が 5 秒以内に完了実行環境に依存

3. テスト手順

  1. Pleiades 上で TestDiceGameStat.java を実行
  2. コンソールに表示された「試行回数」「無料回数」「無料率」「判定メッセージ」を記録
  3. 実行時間をストップウォッチ等で計測
  4. 各テスト項目の期待結果と照合

4. 実施結果


// 実行サンプル
試行回数: 100000
無料回数: 55813
無料率: 55.813%
※無料率は 55%±1% の範囲内です。
  • ST-01:無料率 55.813% → 54~56% の範囲内に収まっている
  • ST-02:例外は発生せず正常終了

5. 合否判定

全項目合格(PASS)