Javaの単体テストはWebアプリでも必要?画面・Controller・Service・DAOの役割で考える

最終更新日

Schoo Java入門 中級 第3回 単体テストによるビジネスロジック検証

本記事は、Schooの「Java入門 中級【2026年版】」第3回「単体テストによるビジネスロジック検証」を学ぶ前後に、Webアプリケーションでも単体テストを行うのかを整理したい方向けの記事です。

単体テストというと、計算クラスやコンソールアプリだけの話に見えるかもしれません。しかし、Webアプリケーションでも単体テストは行います。Webアプリでは、画面、Controller、Service、DAOのように層を分け、それぞれの役割に合わせて確認する内容を考えます。

Schoo コースページ:Java入門 中級【2026年版】

Webアプリでも単体テストは行う

Webアプリは、画面、Controller、Service、DAO、データベースなど、複数の部品がつながって動きます。単体テストでは、その全体をまとめて動かすのではなく、確認したい部品を取り出してテストします。

図解:Webアプリでも単体テストする場所がある
画面
入力・表示
Controller
リクエストを受け取る
Service
ビジネスロジック
単体テストしやすい中心
DAO
DBとのやり取り
Webアプリ全体をブラウザで動かす確認とは別に、Serviceのような一部の処理だけを取り出して単体テストすることがあります。

画面全体を動かすテストとは役割が違う

単体テストは、ブラウザでボタンを押して画面全体の動きを確認するテストとは役割が違います。画面から確認するテストは、入力から表示までの流れを見るのに向いています。一方、単体テストは、特定の処理が仕様どおりに判断しているかを細かく確認するのに向いています。

図解:画面確認と単体テストは役割が違う
画面からの確認
ブラウザで入力して、画面表示や一連の流れを確認する
全体の動きは分かるが、細かい条件を何度も試すには手間がかかる
単体テスト
Serviceのメソッドに値を渡して、結果や例外を確認する
境界値や異常値を繰り返し確認しやすい
Webアプリでは、画面からの確認と単体テストを目的に応じて使い分けます。

Webアプリを層に分けてテスト対象を考える

Webアプリのテストを考えるときは、まず処理を層に分けます。画面、Controller、Service、DAOは、それぞれ担当していることが違います。担当が違うため、テストで確認する内容も変わります。

図解:Controllerを通さずServiceを直接テストする
Webアプリで実行される流れ
Controller → OrderService → DAO
単体テストで確認する部分
new OrderService().calculateShippingFee(5000)
単体テストでは、画面やControllerを必ず通すのではなく、確認したいServiceのメソッドを直接呼び出します。

画面:入力と表示を確認する層

画面は、利用者が値を入力したり、処理結果を見たりする場所です。画面の確認では、入力欄が表示されているか、ボタンを押せるか、エラーメッセージが見えるかなどを確認します。

ただし、画面からすべての境界値や異常値を確認しようとすると手間が大きくなります。たとえば送料計算で 499950005001 を毎回ブラウザから入力して確認するのは効率がよくありません。

Controller:画面から来た値をServiceへ渡す層

Controllerは、画面やリクエストから届いた値を受け取り、必要な形にしてServiceへ渡す層です。Controllerの確認では、「必要な値を受け取れているか」「正しいServiceを呼び出しているか」「結果に応じてどの画面へ返すか」を見ます。

class OrderController {

    private final OrderService orderService;

    OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    String confirm(int amount) {
        int fee = orderService.calculateShippingFee(amount);
        return "送料は" + fee + "円です";
    }
}

このControllerの中心は、送料を計算することではありません。画面から来た amount をServiceへ渡し、Serviceから返ってきた結果を画面向けの文字列にすることです。

Service:ビジネスロジックを確認する層

Serviceは、業務上の判断や計算を担当する層です。送料をいくらにするか、会員ランクで割引率をどう変えるか、在庫不足のときに注文できないようにするか、といった判断をここに置きます。

図解:Serviceは業務上の判断を担当する
画面から届く値
購入金額:12000
会員ランク:GOLD
Serviceのビジネスロジック
送料を0円にするか
割引率を何%にするか
在庫不足なら注文できないようにするか
ビジネスロジックとは、業務上のルールに基づいて結果を決める処理です。WebアプリではServiceにまとめることが多いです。
class OrderService {

    int calculateShippingFee(int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("amount must be 0 or more");
        }

        if (amount >= 5000) {
            return 0;
        }

        return 500;
    }
}

このServiceは、購入金額に応じて送料を決めています。これは画面表示ではなく、業務上のルールに基づく判断です。単体テストでは、この判断が仕様どおりかを直接確認します。

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

class OrderServiceTest {

    @Test
    void testCalculateShippingFee() {
        OrderService service = new OrderService();

        assertEquals(500, service.calculateShippingFee(3000));
        assertEquals(500, service.calculateShippingFee(4999));
        assertEquals(0, service.calculateShippingFee(5000));
        assertEquals(0, service.calculateShippingFee(10000));
    }

    @Test
    void testCalculateShippingFeeThrowsExceptionWhenAmountIsNegative() {
        OrderService service = new OrderService();

        assertThrows(
            IllegalArgumentException.class,
            () -> service.calculateShippingFee(-1)
        );
    }
}

Serviceの単体テストでは、画面やControllerを通さず、メソッドに値を渡して戻り値や例外を確認します。これにより、業務ルールの境界値や異常値を細かく確認できます。

実際の要件からテストケースを作る

WebアプリのServiceをテストするときは、実際の要件をテストケースに変換します。ここでは、在庫数と注文数から注文できるかを判定するServiceを例にします。

在庫数が注文数以上なら注文できる。在庫数が注文数より少ない場合は注文できない。注文数が0以下、または在庫数が0未満の場合は例外とする。

要件と対応するテストケース
No.
要件
入力値
期待結果
確認方法
TC-001
在庫数以上は注文できる
在庫 10 / 注文 3
true
assertTrue
TC-002
在庫数ちょうどは注文できる
在庫 10 / 注文 10
true
assertTrue
TC-003
在庫数を1つ超えると注文できない
在庫 10 / 注文 11
false
assertFalse
TC-004
注文数が0以下なら例外
在庫 10 / 注文 0
例外
assertThrows
TC-005
在庫数が0未満なら例外
在庫 -1 / 注文 1
例外
assertThrows
Serviceの単体テストでは、業務上の要件を1つずつテストケースに対応させます。

この要件では、在庫数と注文数の関係が重要です。特に、在庫数と注文数が同じ場合に注文できるか、注文数が在庫数を1つ超えた場合に注文できないかを確認します。

class StockService {

    boolean canOrder(int stock, int quantity) {
        if (stock < 0) {
            throw new IllegalArgumentException("stock must be 0 or more");
        }

        if (quantity <= 0) {
            throw new IllegalArgumentException("quantity must be 1 or more");
        }

        return stock >= quantity;
    }
}

このServiceに対して、テストケース仕様書の内容をJUnitコードにすると次のようになります。

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;

class StockServiceTest {

    @Test
    void testCanOrderWhenStockIsEnough() {
        StockService service = new StockService();

        assertTrue(service.canOrder(10, 3));   // TC-001
        assertTrue(service.canOrder(10, 10));  // TC-002
    }

    @Test
    void testCannotOrderWhenQuantityIsGreaterThanStock() {
        StockService service = new StockService();

        assertFalse(service.canOrder(10, 11)); // TC-003
    }

    @Test
    void testCanOrderThrowsExceptionWhenValueIsInvalid() {
        StockService service = new StockService();

        assertThrows(IllegalArgumentException.class, () -> service.canOrder(10, 0));  // TC-004
        assertThrows(IllegalArgumentException.class, () -> service.canOrder(-1, 1));  // TC-005
    }
}

このように、要件、テストケース、テストコードを対応させると、何のためにそのテストを書いているのかが分かりやすくなります。

DAO:データベースとのやり取りを確認する層

DAOは、データベースとのやり取りを担当する層です。SQLを実行してデータを取得する、登録する、更新する、削除するといった処理を扱います。

class OrderDAO {

    Order findById(int id) {
        // SQLを実行して、注文データを取得する
    }
}

DAOの確認では、送料計算のような業務ルールではなく、「指定したIDのデータを取得できるか」「SQLの条件が正しいか」「取得結果をオブジェクトに詰められているか」を見ます。

つまり、Webアプリの単体テストでは、層ごとに確認する対象を分けます。Serviceではビジネスロジック、ControllerではServiceへのつなぎ方、DAOではデータベース操作を確認します。

確認問題

次の処理のうち、Serviceの単体テストで確認しやすいものを考えてください。

  • ログイン画面のボタンの色
  • 購入金額に応じた送料計算
  • 会員ランクによる割引率の判定
  • 検索結果が画面に何件表示されるか
  • 在庫数が足りない場合に注文できないようにする判定

演習後の確認:こちらのページを使って、自分で考えた内容を見直せます。

まとめ

  • Webアプリでも単体テストは行う
  • 画面、Controller、Service、DAOは層ごとに役割が違う
  • Controllerは、リクエストを受け取りServiceへつなぐ層
  • Serviceは、ビジネスロジックを置く層
  • DAOは、データベースとのやり取りを担当する層

関連する記事

シェアする