単体テストの実行時間がボトルネックになっている、を解決するテスト影響分析

単体テストやってますか?

個人的な体感では、5年前くらいまでは多くのJava/.NETプロジェクトでは、「やっていない」「一部開発者がやっている」というお話をよく聞いていました。しかし、近頃はプロジェクトで導入している、導入を始めた、というお話を聞くようになりました。※ 当たり前かもしれませんが、ここで言う単体テストはJUnitやNUnitを使って、テストコードを実装する単体テストのことを言っています。

単体テストをやっていない、という方はこれらの過去の記事も参照してみてください。

単体テストの実行に時間がかかるという問題

単体テストのテストケースが増えてくると、必然的に実行時間が長くなってきます。開発者がGUIからテストを実行する場合、実行したいテストケースだけを選択する方法もあります。しかし、修正箇所の影響が広い場合は依存関係を考慮する必要があるため、テストケースを選ぶ手間を考えるとまとめて実行することも多いと思います。すると、テスト実行中はマシンが重くなり大した作業ができないなど開発効率が落ちることに繋がります。それに、テストケースの選択を誤ると、実はコードの変更に関連していたテストが実行されていなかった、という問題が発生する可能性もあります。

また、CIでソースをバージョン管理に登録したタイミングでテストが走る場合も、頻繁にテストが走ると完了まで時間がかかりテストの実行を待つ必要があります。ここで問題が発生すると、再度同じ手順を踏む必要があり、スピード感のある開発の妨げになります。

この様な課題を解決する機能がParasoft Jtest/dotTESTのテスト影響分析の機能です。

テスト影響分析とは?

テスト影響分析の機能は修正したソースコードの影響を受けるテストケースだけを自動で選んで実行する機能です。
Jtest/dotTESTはテストケースごとにカバレッジを紐づけて管理しています。そのため、テスト対象のあるメソッドを修正すると、そこから逆引きして実行すべきテストケースだけを自動で実行してくれます。実行するテストを絞り込むため、実行にかかる時間の削減に繋がります。また、修正のたびにテストケースを手動で選択する手間を省き、テストの実行漏れを防ぎます。

テスト影響分析の実行例

オンイランショッピングを行うアプリケーションを例に説明します。商品の名称、金額などを格納するItemクラスに修正を加えます。単体テスト実行はItemクラスのテストケースに加えて、それを参照するCartクラスのテストケースがテスト影響分析によって実行される例です。

これが修正対象となるItemクラスです。

public class Item {
    /**
     * 商品クラスのコンストラクタ
     * @param name 商品名
     * @param id 商品 ID
     * @param price 値段
     * @param count 1 商品の購入する数
     */
    public Item(String name, String id, int price, int count) {
        super();
        this.name = name;
        this.id = id;
        this.price = price;
        this.count = count;
    }
… … …

Cartクラスは「買い物かご」のクラスです。そのため、商品であるItemクラスとは依存関係にあります。以下はCartクラスのgetItemメソッドです。このメソッドは商品IDをキーに該当するItemクラスを返すメソッドです。

public Item getItem(String itemId) {
    Item item = null;

    List itemList = getItemList();
    for (int i = 0; i < itemList.size(); i++) {
        item = itemList.get(i);
        if (item.getId().equals(itemId)) {
            break;
        } else {
            item = null;
        }
    }
    return item;
}

ここで、仕様変更として、Itemクラスに割引率をもたせるようにソースコードを修正します。クラスフィールドにdiscountRateを追加しています。実際にはsetter/getterなども追加していますが、ここでは割愛します。

public class Item {
    private double discountRate;
    /**
     * 商品クラスのコンストラクタ
     * @param name 商品名
     * @param id 商品 ID
     * @param price 値段
     * @param count 1 商品の購入する数
     */
    public Item(String name, String id, int price, int count) {
        super();
        this.name = name;
        this.id = id;
        this.price = price;
        this.count = count;
        this.discountRate = 0;
    }
… … …

修正が完了したところで単体テストを実行します。実施対象はItemクラスの単体テストケースですが、依存関係のある他のクラスも動作が変わっていないか確認したい。しかし、Itemクラスを参照しているクラスを探し、そのクラスのテストケースを選んで実行するのは面倒です。人為的なミスが発生する可能性もあります。そこで、Jtestのテスト影響分析を利用します。

Jtestでは下図の様にテストケースとその実行によってカバーされるソースコードが管理されています。そのため、修正したソースコードから実行すべきテストケースを確認することができます。

Eclipse上からでも実行すべき単体テストを確認でき、ボタンをクリックするだけで必要なテストケースが実行されます。

しかし、最終的にはすべてのテストケースを実行したいでしょう。そんなときは、たとえば開発中の開発者の環境ではテスト影響分析によって最低限必要なテストケースだけ実行します。すべてのテストケースの実行はCIを使って、プルリクやメインへマージのタイミングなどイベントが発生した時だけや、夜間や週末など時間があるときに定期実行する、といった運用が考えられます。

まとめ

単体テストは導入後も様々な課題が出てくることがあります。今回はテスト実行に時間がかかるという課題に焦点をあててテスト影響分析の機能をご紹介しました。
Parasoft Jtest/dotTESTは単体テストだけではなく、静的解析や結合・システムテストのテスト漏れを確認する機能などJava/.NET開発を助ける機能を持っています。効率的な開発を行うためにもぜひJtest/dotTESTをお試しください。

Top