Webアプリの仕様変更はどこを直す?修正箇所と影響範囲の調べ方

最終更新日

Schoo Java入門 中級 第10回 Webアプリケーション実装演習

「学習時間を、480分までではなく720分まで登録できるようにしてほしい」。画面の入力欄だけを直しても、Java側に480分の制限が残っていれば、600分を登録しようとしたときにエラーになります。

仕様変更では、数字の書き換えより先に、そのルールがどこで使われ、何を確かめ直す必要があるかを調べます。この記事では、学習記録Webアプリを例に、修正箇所の探し方、変更前後のコード、テストケースの更新までを具体的にたどります。

本記事は、Schoo「Java入門 中級」第10回「Webアプリケーション実装演習」を学んだあとに理解を深めるステップアップ学習記事です。未受講の方も読めるよう、必要な仕様とコードを本文に示します。配布プログラムのダウンロードは前提にしていません。

まず「何を変え、何を変えないか」を決める

「720分まで登録できる」には、1件の記録の上限なのか、1日分の合計なのかという違いがあります。ここでは1件の学習記録に入力できる時間の上限だけを変更することにします。1日の合計時間に上限を設ける変更ではありません。

項目今回の変更内容
変更前1件の学習時間は1〜480分の整数。
変更後1件の学習時間は1〜720分の整数。
変えないこと単位は分、入力は必須、0・負数・小数・数値でない入力は不可。ログイン確認や他の入力項目のルールも維持する。
保存と表示入力した分数をそのまま保存し、一覧にも同じ分数を表示する。過去の記録を書き換えたり、時間単位に変換したりしない。
完了の判断画面から720分を登録・再表示でき、サーバーへ721分が届いた場合は保存せず拒否する。変更していない条件も引き続き守る。

以下は、この変更依頼を扱う独立した例です。以前の記事で示した「1〜480分」という変更前の仕様を、誤りとして書き換える話ではありません。

図解:画面を直しても、Java側の制限は変わらない

上:画面は720分まで入力できても、600を受け取ったJavaが480分超として拒否し、保存されない。下:Javaも720分上限に変更すると600は範囲内となり、600分を保存できる。
ほかの入力が有効で、DBも600分を受け入れられる場合の比較です。図のJavaコードは上限チェック部分だけを抜き出しています。

ブラウザの入力制限と、サーバーで動くJavaの入力チェックは別の処理です。HTMLのmaxを720にしても、Javaのif文は変わりません。逆にJavaだけを直すと、画面が600分の送信を止めることがあります。

画面の制限は入力ミスを減らすために役立ちますが、通常の画面を通らないリクエストも届き得ます。画面で制限しているからといって、サーバー側のチェックを削除してはいけません。

1.数字だけでなく、値が通る場所を調べる

最初にプロジェクト全体で480を検索します。ただし、検索結果をすべて720へ置き換えるわけではありません。480が「学習時間の上限」なのか、「過去に登録した480分のデータ」なのかを区別します。

検索する言葉探すもの
480、480分、8時間入力上限、案内文、エラーメッセージ、設計書。8時間は480分の別表現なので、数字だけの検索では見落とすことがある。
studyMinutes画面のname属性、Servletの取得箇所、Javaの変数・DTOのフィールド。
convertStudyMinutes、registerStudyLog時間の変換・チェックをするメソッドと、それを呼ぶ登録処理。
study_minutesDAOのSQL、DBの列定義、初期化用SQL。JavaとDBで名前の書き方が違う場合がある。
481、上限、境界値変更前の「上限超過」を確認するテストと、テスト仕様書の期待結果。

EclipseなどのIDEでは、文字列検索に加えて、メソッドの宣言や呼び出し元へ移動する機能も使えます。登録ボタンから順に入力欄 → Servlet → Model → DAO → DB → 一覧表示をたどり、同じ時間の値がどこへ渡るかを確認します。Modelは、入力の妥当性や業務上のルールを扱う部分です。

例えば入力欄の名前がstudyMinutesなら、Servletのrequest.getParameter("studyMinutes")を探せます。ここで取得する値は文字列です。その文字列をどのModelへ渡し、どこで整数へ変換し、どこで上限を判定しているかを追います。

2.修正する場所と、確認だけでよい場所を分ける

調べた結果を、次のように残します。「影響範囲に入る」ことと「コードを変更する」ことは同じではありません。修正不要と判断した理由も書くと、見落としたのか、確認して不要としたのかが分かります。

対象この例での判断と理由
study-log-form.jsp修正する。入力欄のmaxと「480分まで」の案内を720分へ変更する。
StudyLogModel.java修正する。convertStudyMinutes内の上限判定と、上限を説明するエラーメッセージを変更する。
StudyLogRegisterServlet.javaこの例では修正不要。文字列を取得してModelへ渡す処理と、エラー時の案内先は変わらない。独自の上限判定があれば別途修正する。
StudyLog.java(DTO)この例では修正不要。studyMinutesはint型で、720も表せる。項目名・型・単位は変えない。
StudyLogDao.javaこの例では修正不要。受け取った整数を同じ列へ保存するSQLを使う。SQL内の480制限がないことを確認する。
DBのstudy_minutes列定義を確認して判断する。INTEGER型で720を扱え、上限480の制約がなければ今回のための構造変更は不要。制約があれば変更が必要。
一覧JSP分数をそのまま出すだけなら修正不要。ただし600・720が保存後にも同じ値で表示されるか確認する。
テスト・仕様書修正する。481の期待結果、新しい境界値720・721、メッセージ、入力条件を更新する。

この表は役割ごとの例です。自分のアプリでServletにも上限チェックを書いているなら、そこも対象です。集計・CSV出力・編集機能などで同じ値を扱っていれば、その利用先も追います。「この表にないから対象外」とは判断しません。

3.画面の入力制限と案内文をそろえる

study-log-form.jsp:学習時間の入力欄(変更前)

<label for="studyMinutes">学習時間(分)</label>
<input type="number" id="studyMinutes"
       name="studyMinutes" min="1" max="480"
       step="1" required>
<p>1〜480分の整数で入力してください。</p>

同じ入力欄(変更後)

<label for="studyMinutes">学習時間(分)</label>
<input type="number" id="studyMinutes"
       name="studyMinutes" min="1" max="720"
       step="1" required>
<p>1〜720分の整数で入力してください。</p>

変更するのは上限と案内文です。下限のmin="1"、整数刻みのstep="1"、必須指定のrequiredは残します。name="studyMinutes"を変える必要もありません。名前を変えると、Servletの取得箇所との対応が崩れます。

4.Javaの判定とエラーメッセージを変更する

StudyLogModel.java:整数への変換後にある範囲チェック(変更前の抜粋)

if (studyMinutes < 1 || studyMinutes > 480) {
    throw new IllegalArgumentException(
        "学習時間は1分以上480分以下で入力してください。"
    );
}

同じ範囲チェック(変更後の抜粋)

if (studyMinutes < 1 || studyMinutes > 720) {
    throw new IllegalArgumentException(
        "学習時間は1分以上720分以下で入力してください。"
    );
}

studyMinutesは入力された学習時間を整数へ変換した値です。> 720なので720は通り、721は拒否されます。>= 720にすると、登録できるはずの720まで拒否してしまいます。

条件だけ直し、メッセージを「480分以下」のままにすると、利用者への案内と実際のルールが食い違います。一方、studyMinutes < 1や整数への変換、未入力の確認は変更しません。上限を広げる変更で、他の入力チェックまで緩めないことが大切です。

5.DBは「整数だから大丈夫」で終わらせない

720という数値を保存できる型でも、DB側にCHECK (study_minutes BETWEEN 1 AND 480)のような制約があれば登録できません。CHECK制約は、保存する値が条件を満たすことをDB側でも確認する仕組みです。

  • 実際に接続するDBの列定義・制約を確認する。作成用SQLファイルだけ見て、稼働中のDBも同じだと決め付けない。
  • 上限480の制約がある場合は、既存データを保ったまま720へ広げる移行手順を用意する。DB製品により変更方法が異なるため、検証用DBで試す。
  • 新しくDBを作るためのSQLや、テスト用DBの初期化定義も新仕様へ合わせる。既存DBだけ直すと、新規環境で古い制限が復活する。
  • 480分として保存された過去の記録は480分のままにする。仕様変更は、記録された学習時間そのものを書き換える依頼ではない。

制約が存在しなければ、今回の上限変更だけを理由にDB構造やINSERT文を変える必要はありません。反対に、以前の記事のようにDBにも上限を持たせた構成なら、その制約も対象です。本番DBを削除して作り直したり、初期化用プログラムを本番で実行したりして対応しないでください。

6.テストケースは「新しい上限」だけでなく「古い境界」も見る

変更前に481分を拒否していたテストは、新仕様ではそのまま使えません。481分を受け入れる確認へ変え、上限超過のケースには721分を追加します。720だけ試すより、変更によって結果が変わる値と、変わらない値を並べると確認の意図がはっきりします。

入力値変更前 → 変更後/確認したいこと
0拒否 → 拒否。下限を壊していない。
1許可 → 許可。最小値を引き続き登録できる。
480許可 → 許可。古い上限だった値も有効。
481拒否 → 許可。480分の制限が残っていない。
600拒否 → 許可。新たに使える範囲の代表値で保存・再表示まで確認する。
720拒否 → 許可。新しい上限そのものを受け入れる。
721拒否 → 拒否。新しい上限を超える値を受け入れない。
空欄拒否 → 拒否。必須条件を維持する。
1.5、abcどちらも拒否 → 拒否。整数でない入力を受け入れない。

表の「許可」は時間の値として妥当という意味で、他の必須項目が不正でも登録できるという意味ではありません。また、Modelの単体テストで値を許可しただけでは、DBへの保存まで確認したことにはなりません。

7.変更したルールをJUnitのテストへ落とし込む

次は、未入力の確認・整数への変換・範囲チェックをStudyMinutesRule.javaという小さな教材用クラスにまとめた例です。Webアプリ全体ではなく、学習時間のルールだけをテストします。既存のModelを必ずこのクラスへ作り替える指示ではありません。

StudyMinutesRule.java。convertは入力文字列を受け取り、妥当なら整数を返し、不正なら例外で通知します。

public class StudyMinutesRule {
    public int convert(String text) {
        if (text == null || text.trim().isEmpty()) {
            throw new IllegalArgumentException(
                "学習時間を入力してください。"
            );
        }

        int studyMinutes;
        try {
            studyMinutes = Integer.parseInt(text);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                "学習時間は整数で入力してください。"
            );
        }

        if (studyMinutes < 1 || studyMinutes > 720) {
            throw new IllegalArgumentException(
                "学習時間は1分以上720分以下で入力してください。"
            );
        }
        return studyMinutes;
    }
}

StudyMinutesRuleTest.java。同じクラスをJUnit Jupiterでテストします。各テストは、上の表の確認事項に対応しています。

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

class StudyMinutesRuleTest {
    private final StudyMinutesRule rule = new StudyMinutesRule();

    @Test
    void acceptsOneMinute() {
        assertEquals(1, rule.convert("1"));
    }

    @Test
    void rejectsZeroMinutes() {
        assertThrows(IllegalArgumentException.class,
            () -> rule.convert("0"));
    }

    @Test
    void acceptsOldUpperLimit() {
        assertEquals(480, rule.convert("480"));
    }

    @Test
    void acceptsJustAboveOldUpperLimit() {
        assertEquals(481, rule.convert("481"));
    }

    @Test
    void acceptsNewUpperLimit() {
        assertEquals(720, rule.convert("720"));
    }

    @Test
    void rejectsAboveNewUpperLimit() {
        IllegalArgumentException error = assertThrows(
            IllegalArgumentException.class,
            () -> rule.convert("721")
        );
        assertEquals(
            "学習時間は1分以上720分以下で入力してください。",
            error.getMessage()
        );
    }

    @Test
    void rejectsBlankInput() {
        assertThrows(IllegalArgumentException.class,
            () -> rule.convert(""));
    }

    @Test
    void rejectsDecimalInput() {
        assertThrows(IllegalArgumentException.class,
            () -> rule.convert("1.5"));
    }

    @Test
    void rejectsNonNumericInput() {
        assertThrows(IllegalArgumentException.class,
            () -> rule.convert("abc"));
    }
}

assertEqualsは期待した分数が返るか、assertThrowsは指定した例外で拒否されるかを確認します。() -> rule.convert("721")は、確認したい処理をJUnitへ渡す書き方です。721のケースではエラーメッセージも確認しています。

この2ファイルはJUnit Jupiterを利用するJavaのテスト環境を前提とします。ServletやDBは使いません。JUnitの基本はJavaのJUnitテストの書き方とは?@Testからテストメソッドの作り方まで理解する、例外の確認はJavaのassertThrowsとは?例外が起きることをテストする考え方で扱っています。

変更前のテスト結果を消して帳尻を合わせるのではなく、「どの要件が変わったため、どの期待結果を変えたか」を記録します。例えば「CH-01:上限を720分へ変更したため、481分を拒否から許可へ変更」と残せます。テストが失敗したから期待値を変えるのではなく、合意した新仕様に基づいて変えます。

8.画面・保存・再表示までつながるか確認する

単体テストが通ったら、検証用Webアプリで登録から一覧までを確かめます。次のように事前条件と期待結果を書けば、「720を入れてみた」で終わりません。

項目CH-01-S1:600分を登録するテスト仕様書の例
目的新たに有効となった分数を、画面から登録し、保存後も同じ値で表示できること。
事前条件検証用環境に修正版を配置済み。利用者1001でログイン。本人の既存記録2件、他利用者1件、全体3件。変更後のDB定義を確認済み。
入力学習日は操作日、カテゴリJava、内容「例外処理の復習」、学習時間600、メモ空欄。時間以外も有効な値を使う。
手順登録画面の案内が720分までであることを確認する。上記の値を入力して1回登録し、完了画面から一覧を取得し直す。
画面の期待結果登録が成功し、新しい記録の時間が600分と表示される。480に切り詰められたり、10分と表示されたりしない。
DBの期待結果利用者1001の記録が2→3件、全体3→4件。追加行の所有者とstudy_minutes=600を確認する。他利用者の記録と既存行は変更されない。
記録対象の版、操作日時、実施者、入力、画面とDBの実際の結果を残す。表は実施例の設計であり、実行済みの結果ではない。

720分でも同じ流れを確認します。各ケースは既知の初期状態へ戻すか、開始件数を別途記録して独立させます。また、変更前に保存した480分の記録が、修正後も480分のまま読めることを確認します。

確認する層721分の扱い
ブラウザ通常のフォーム送信ではmaxの制限により送信を止めることを確認する。
サーバー721分が実際に届くテスト用リクエストで、理由を示して拒否し、DBの件数・内容が変わらないことを確認する。
判定時の注意ブラウザが送信を止めた結果だけで、サーバー側のテストを合格にしない。サーバーへ送れていなければ、そのケースは未実施として残す。

サーバー側の確認は検証環境で、送信先・POSTなどの送信方法・パラメータ名・値・ログイン状態を定めて行います。通常の画面が721を送れないからといって、製品の入力制限を削除した状態で公開しないでください。

9.変更していない機能の再確認と、完了報告を残す

変更した箇所の確認に加え、既存機能が壊れていないかを確かめるのが回帰テストです。今回なら、0分の拒否、通常の30分登録、未ログイン時の登録拒否、他利用者の記録が見えないことなどが候補になります。既存の自動テストも実行し、手動の確認範囲は影響と重要度に応じて決めます。

  • 変更依頼:1件の上限を480分から720分へ。単位・下限・必須条件は維持。
  • 修正箇所:画面、Model、案内文、テスト・仕様書。DB制約の変更要否と判断根拠も記録。
  • 確認結果:単体テスト、画面での600・720分登録、721分の拒否と未保存、既存記録の保持。実際の結果と対象版を付ける。
  • 残る課題:未実施や未解決のものを記載する。未確認なのに「影響なし」「すべて完了」としない。

公開時は、画面・Java・DBの制約が別々の版にならないよう反映手順もそろえます。720分のデータを保存したあとに旧版へ戻す場合も、そのデータの読み取り・編集ができるかを考える必要があります。単にコードを元に戻せばよいとは限りません。

確認問題

  1. 画面のmaxを720に変えましたが、600分の登録がエラーになります。次に確認する場所を、値の流れに沿って挙げてください。
  2. プロジェクト内の480を検索すると、Javaの上限判定、画面の説明文、既存記録の学習時間が見つかりました。すべて720へ置き換えてよいですか。それぞれの扱いを答えてください。
  3. 変更前のテストには「481分なら例外になる」とあります。変更後の期待結果と、新たに必要な上限付近のテスト値を答えてください。
  4. DTOはint型、DAOは受け取った数値をそのまま保存します。DBにはCHECK (study_minutes BETWEEN 1 AND 480)があります。どこを変更・確認する必要がありますか。
  5. 721分を入力するとブラウザが送信を止めました。また、JUnitの時間チェックはすべて成功しました。これでWebアプリの仕様変更を完了にしてよいですか。追加する確認を答えてください。

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

まとめ

  • 最初に変更するルールと、変えないルールを分ける。
  • 数字だけでなく、画面から保存・再表示まで値が通る場所をたどる。
  • 修正する箇所だけでなく、修正不要と判断した根拠も残す。
  • 古い境界値・新しい境界値・変えない条件をテストする。
  • 画面表示だけでなく、保存内容・未保存・既存データへの影響まで確認する。

関連記事

シェアする