16
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI時代のSEに残る仕事は「責任を持つ」ことかもしれない 〜単体テストを題材に〜

16
Last updated at Posted at 2026-04-20

AI時代のSEに残る仕事は「責任を持つ」ことかもしれない 〜単体テストを題材に〜

投稿内容は私個人の見解に基づくものであり、所属企業・部門見解を代表するものではありません。

はじめに

AI にコード生成を任せる場面がかなり増えてきました。実装だけを見ると、かなりの部分を AI が肩代わりできるようになっています。

その一方で、人間に残る仕事は何かと考えると、私は「責任を持つこと」がかなり大きいのではないかと思っています。

今回はその話を、単体テストを題材に考えてみます。
この記事では単体テストの厳密な定義よりも、AI と人間の役割分担を考えることを目的にします。

題材のコード

今回は、ポイント計算を行う次のクラスを使います。

LoyaltyPointCalculator.java
public class LoyaltyPointCalculator {

    /**
     * 購入金額とユーザー属性から、今回付与するポイントを計算する
     */
    public int calculatePoints(long amount, String memberStatus, boolean isCampaignDay, boolean hasCoupon) {
        if (amount < 0) {
            throw new IllegalArgumentException("金額が負の値です");
        }

        double rate = switch (memberStatus) {
            case "PLATINUM" -> 0.10; // 10%
            case "GOLD" -> 0.05;     // 5%
            default -> 0.01;         // 1%
        };

        double finalRate = (isCampaignDay && !"PLATINUM".equals(memberStatus)) ? rate * 2 : rate;

        long points = Math.round(amount * finalRate);

        if (hasCoupon && amount >= 1000) {
            points += 100;
        }

        return (int) Math.min(points, 5000);
    }
}

仕様として読み取れそうなのは、たとえば次のような内容です。

  • 会員ランクごとに還元率が違う
  • キャンペーン日は一部会員だけ倍率がかかる
  • 1000 円以上でクーポン加算がある
  • 付与ポイントには上限がある
  • 負の値の金額は例外にする

実験

同じクラスに対して、AI に 2 通りの頼み方をしました。
IDEはIntelliJ IDEA、モデルはClaude Sonnet 4.6です。

1. 雑に依頼する

プロンプトはこれだけです。

LoyaltyPointCalculatorクラスに単体テストを被せて。

この場合、AI はかなり広めに解釈して、会員ランク、キャンペーン日、クーポン、上限、境界値、異常系、複合条件まで含めた長いテストを生成しました。

実際に生成されたコードは250行近くありましたが、抜粋すると次のようになります。
import文は省略しています。

LoyaltyPointCalculatorTest.java
@DisplayName("LoyaltyPointCalculator テスト")
class LoyaltyPointCalculatorTest {

    private LoyaltyPointCalculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new LoyaltyPointCalculator();
    }

    @Nested
    @DisplayName("会員ランク別のポイントレート")
    class MemberStatusRateTest {

        @Test
        @DisplayName("REGULAR会員は購入金額の1%がポイントになる")
        void regularMemberGets1Percent() {
            assertEquals(100, calculator.calculatePoints(10000, "REGULAR", false, false));
        }

        @Test
        @DisplayName("GOLD会員は購入金額の5%がポイントになる")
        void goldMemberGets5Percent() {
            assertEquals(500, calculator.calculatePoints(10000, "GOLD", false, false));
        }

        @Test
        @DisplayName("PLATINUM会員は購入金額の10%がポイントになる")
        void platinumMemberGets10Percent() {
            assertEquals(1000, calculator.calculatePoints(10000, "PLATINUM", false, false));
        }

        @Test
        @DisplayName("未知の会員ランクはREGULARと同様に1%になる")
        void unknownStatusGets1Percent() {
            assertEquals(100, calculator.calculatePoints(10000, "SILVER", false, false));
        }
    }

    @Nested
    @DisplayName("異常系: 不正な入力値")
    class ExceptionTest {

        @Test
        @DisplayName("購入金額が負の値の場合はIllegalArgumentExceptionをスローする")
        void negativeAmountThrowsIllegalArgumentException() {
            assertThrows(IllegalArgumentException.class,
                () -> calculator.calculatePoints(-1, "REGULAR", false, false));
        }
    }
}

良かった点は、観点が広いことです。少なくともコードの分岐はかなり丁寧に拾ってくれます。

ただ、気になった点もありました。たとえば「未知の会員ランクは REGULAR と同様に 1%」というテストは、コード上は正しいです。が、仕様として意図されているものかどうかは不明確です。

そして、仕様が曖昧な状態で250行近いテストをレビューする側は、確認観点が増えて負荷が高くなります。

2. テストケースを明示して依頼する

次は、先に私がテストケースを整理してから依頼しました。

test_case.csvに以下のテストケースを記載した。LoyaltyPointCalculatorクラスに単体テストを被せて。

与えたケースは次の 8 件です。

ケースID 購入金額 会員ランク キャンペーン日 クーポン 期待されるポイント
TC-01 1,000 REGULAR FALSE FALSE 10
TC-02 1,000 GOLD FALSE FALSE 50
TC-03 1,000 GOLD TRUE FALSE 100
TC-04 1,000 PLATINUM TRUE FALSE 100
TC-05 999 REGULAR FALSE TRUE 10
TC-06 1,000 REGULAR FALSE TRUE 110
TC-07 100,000 PLATINUM FALSE FALSE 5,000
TC-08 -1 ANY - - Error

この場合、AI はそのテストケースを素直にコード化した、かなりシンプルなテストを返しました。

実際の生成コードは次の通りです。これは全コードです。
import文は省略しています。

LoyaltyPointCalculatorTest.java
@DisplayName("LoyaltyPointCalculator 単体テスト")
class LoyaltyPointCalculatorTest {

    private final LoyaltyPointCalculator calculator = new LoyaltyPointCalculator();

    @DisplayName("正常系: ポイント計算")
    @ParameterizedTest(name = "[{index}] {0}: 購入金額={1}, ランク={2}, キャンペーン={3}, クーポン={4} → 期待ポイント={5}")
    @CsvSource({
        "TC-01, 1000,    REGULAR,  false, false, 10",
        "TC-02, 1000,    GOLD,     false, false, 50",
        "TC-03, 1000,    GOLD,     true,  false, 100",
        "TC-04, 1000,    PLATINUM, true,  false, 100",
        "TC-05, 999,     REGULAR,  false, true,  10",
        "TC-06, 1000,    REGULAR,  false, true,  110",
        "TC-07, 100000,  PLATINUM, false, false, 5000"
    })
    void testCalculatePoints(String caseId, long amount, String memberStatus,
                             boolean isCampaignDay, boolean hasCoupon, int expectedPoints) {
        int actual = calculator.calculatePoints(amount, memberStatus, isCampaignDay, hasCoupon);
        assertEquals(expectedPoints, actual, caseId + " のポイント計算結果が一致しません");
    }

    @Test
    @DisplayName("異常系: 負の購入金額は IllegalArgumentException")
    void TC_08_negativeAmountThrowsException() {
        assertThrows(IllegalArgumentException.class,
            () -> calculator.calculatePoints(-1, "REGULAR", false, false),
            "TC-08: 負の金額で IllegalArgumentException がスローされるべきです"
        );
    }
}

こちらの良かった点は、何を確認したいテストなのかが明確なことです。保守もしやすいと思います。

一方で、当然ながら与えたケースしか検証しません。つまり、網羅性の責任は人間側に残ります。
このケース群では、コード上の主要な分岐をすべて確認できました。

比較して見えたこと

この 2 つを比べると、AI は依頼内容にかなり忠実に応答してくれます。だからこそ、最終的に「それを仕様として採用するか」は人間の判断が必要です。

  • 曖昧に頼めば、AI はコードを読んでそれっぽく広く補完する
  • 具体的に頼めば、AI は与えられたケースを正確にコード化する

どちらも便利です。ただし、「このテストが仕様として妥当か」を最終判断する役割は、今のところ人間に残ります。

たとえば上司に「単体テストを作って」と言われたとき、私は 2 つ目のほうが自信を持って提出しやすいです。

理由はシンプルで、自分が責任を持てるからです。

1 つ目は、たしかに広く確認できています。でも、その中には「コードはそうなっているが、本当に仕様なのか分からないもの」も混ざります。そこに責任を持つのは難しいです。

2 つ目は、人間が仕様として確認したいケースを定義し、その実装だけを AI に任せています。責任の所在がはっきりしています。

まとめ

今回の実験で感じたことは次の通りです。

  • AI はコードを読んで、それらしいテストをかなり速く作れる
  • ただし、仕様の妥当性や網羅性を引き受けるのは人間
  • 人間の仕事は減るというより、「どこに責任を持つか」がより重要になる

AI に実装を任せること自体は、もう特別なことではないと思います。

そのうえで、人間がやるべきなのは「何を仕様とみなすのか」「どこまでを保証するのか」を決め、その判断に責任を持つことではないでしょうか。

みなさんなら、どちらのテストを提出しますか。

16
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?