2
2

More than 3 years have passed since last update.

【Java発展学習12日目】テストの分類とその品質保証、リファクタリングについて

Last updated at Posted at 2021-07-15

品質段階

プログラムの開発品質を判定する段階は、以下の通り。

段階 状態 内容 判定方法
1 コンパイルエラー 文法上の誤り コンパイル
2 ランタイムエラー・異常終了 プログラム実行中に異常終了が発生 テスト
3 仕様非準拠 正常終了するものの仕様通りでない テスト
4 完成 全機能が仕様通りに正常終了 テスト

4段階のテスト

テスト段階別に分類すると、以下のようになる。

テスト テスト対象
単体テスト(UT; Unit Test) ある一つの「クラス」または「機能」
結合テスト(IT; Integration Test) 単体テストが完了した複数の「クラス」・「機能」
総合テスト(ST; System Test) 結合テストが完了した全ての「クラス」・「機能」
受け入れテスト(UAT; User Acceptance Test) システム一式 ( 発注者が実施 )

3種類のテスト

テスト目的別に分類すると、以下のようになる。

テスト 実施者 内容
顧客要件テスト 顧客またはプロジェクト 仕様通りの機能であることを確認
品質保証テスト プロジェクト 性能および信頼性の改善・保証
開発者テスト 開発者 設計および開発の洗練

テストケース(test case)

テストケース正常系異常系の2種類を組み合わせて作成する。

同値分割(equivalence partitioning)は、有効値無効値の集合の中から、任意の代表値を選択し使用する手法であり、
境界値分析(boundary value analysis)は、条件分岐の境界値付近の値を使用する手法である。


MavenプロジェクトでのJUnitの利用(VSCode)

Visual Studio CodeMavenプロジェクトJUnitを導入する場合、
テストクラスで利用するAssertクラスの静的メソッドと、
テストメソッドを表す@Testアナテイションを定義するTestクラスをインポートする必要がある。

サンプルコード

src/main/TestClass.java
public class TestClass {
    private String str = "";
    private int i = 0;

    // コンストラクタ
    public TestClass(){}
    public TestClass(String str) {
        this.str = str;
    }
    public TestClass(int i) {
        this.i = i;
    }
    public TestClass(String str, int i) {
        this.str = str;
        this.i = i;
    }

    // ゲッタ
    public String getStr() {
        return this.str;
    }
    public int getI() {
        return this.i;
    }

    // セッタ
    public void setStr(String str) {
        this.str = str;
    }
    public void setI(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "(" + this.str + ", " + this.i + ")";
    }

    public void plus(int i) {
        this.i += i;
    }
    public void minus(int i) {
        this.i -= i;
    }
}
src/test/TestClassTest.java
import static org.junit.Assert.*;
import org.junit.Test;

public class TestClassTest {
    // インスタンス化テスト
    // -> テストメソッドには@Testアナテイションを付与
    @Test
    public void instantiate() {
        // インスタンスの生成
        TestClass tc = new TestClass("ABC", 123);

        assertEquals("ABC", tc.getStr());
        assertEquals(123, tc.getI());
    }

    // 増減テスト
    @Test
    public void plusMinus() {
        // インスタンスの生成
        TestClass tc = new TestClass();

        tc.setI(123);

        tc.plus(27);
        assertEquals(150, tc.getI());
        tc.minus(27);
        assertEquals(123, tc.getI());
    }
}
JUnitによるテスト
% mvn test
[INFO] Scanning for projects...
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running TestClassTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.02 s - in TestClassTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.314 s
[INFO] Finished at: 2021-07-15T13:10:04+09:00
[INFO] ------------------------------------------------------------------------

アサーション(assertion)

アサーションを用いることで、テスト対象クラスの各メソッドに対して、想定値でない場合はプログラムを強制停止させることができる。

ただし、アサーション機能を有効するためには-eaオプションをつけてクラスファイルを実行する必要がある。

アサーション機能を有効にしたプログラム実行
% java -ea <メインクラスのFQCN>

契約による設計(DbC; Design by Contract)

主なアサーションの利用場面は、以下の通り。

場面 内容
呼び出し時 引数が正常値(=nullでない)であることを保証
処理の終了時 戻り値や実行結果が正常値であることを保証
常時 インスタンスの内部状態が正常値であることを保証

サンプルコード

TestClass.java
public class TestClass {
    private String str = "";
    private int i = 0;

    // コンストラクタ
    public TestClass(){}
    public TestClass(String str) {
        this.str = str;
    }
    public TestClass(int i) {
        this.i = i;
    }
    public TestClass(String str, int i) {
        this.str = str;
        this.i = i;
    }

    // ゲッタ
    public String getStr() {
        return this.str;
    }
    public int getI() {
        return this.i;
    }

    // セッタ
    public void setStr(String str) {
        this.str = str;
    }
    public void setI(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "(" + this.str + ", " + this.i + ")";
    }

    public void plus(int i) {
        this.i += i;

        if (i >= 0) {
            // アサーションによるテスト
            assert this.i >= (this.i - i);
        }
        else {
            assert this.i < (this.i - i);
        }
    }
    public void minus(int i) {
        this.i -= i;

        if (i >= 0) {
            // アサーションによるテスト(エラーメッセージつき)
            assert this.i <= (this.i + i) : "calculation has failed.";
        }
        else {
            assert this.i > (this.i + i) : "calculation has failed.";
        }
    }
}

メトリクス

ソフトウェア開発で利用される主なメトリクスは、以下の通り。

メトリクス 視覚化対象
コードカバレッジ テスト品質
LOC(Lines of Code) プログラム規模
CC(Cyclomatic Complexity; 循環的複雑度) メソッドの複雑度
※分岐・ループ処理によって数値が上昇
WMC(Weighted Method Per Class) クラス内の全メソッドのCCの合計値

コードカバレッジ(code coverage)

テスト品質のメトリクスとなるコードカバレッジの種類は、主に以下の3つ。

レベル カバレッジ 計算式
C0 命令網羅率 テストで通過する命令数 / コード全体の命令数 × 100 [%]
C1 分岐網羅率 テストで通過する分岐経路数 / コード全体の分岐経路数 × 100 [%]
C2 条件網羅率 テストで通過する単純分岐経路数 / コード全体の複数条件分岐経路数 × 100 [%]

Jacoco

カバレッジを計測する場合、OSSであるJacocoライブラリを利用する。

また、MavenプロジェクトJacocoを利用する場合、POMファイルは以下のように追記することで、
MavenセントラルからJacocoのJARファイルを自動でダウンロードすることができる。

pom.xml
<!-- 追記箇所(dependenciesタグ) -->
<dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
</dependency>

<!-- 追記箇所(build/pluginManagement/pluginsタグ) -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
</plugin>
Jacocoによるカバレッジ計測
% mvn jacoco:prepare-agent test jacoco:report
[INFO] Scanning for projects...
...
[INFO] --- jacoco-maven-plugin:0.8.7:report (default-cli) @ maven ---
[INFO] Loading execution data file <Mavenプロジェクトのディレクトリパス>/target/jacoco.exec

[INFO] Analyzed bundle 'maven' with 2 classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.804 s
[INFO] Finished at: 2021-07-15T15:43:33+09:00
[INFO] ------------------------------------------------------------------------

Jacocoによるカバレッジ計測を実行すると、target/site/jacocoフォルダに
カバレッジ計測結果が記述されたHTMLファイルが自動生成される。

Jacoco.png

Jacoco2.png


リファクタリングの手法

主なリファクタリング手法は、以下の通り。

  1. 重複部分をメソッドで再定義
  2. メソッドクラスの分割
  3. 外部ライブラリや直近バージョンのJavaライブラリでの代用
  4. コメントの修正

静的コード解析(static code analysis)

静的コード解析を行う主な開発ツールは、以下の通り。

ツール 解析内容
SpotBugs バグが生じる可能性のある記述
Checkstyle コーディングスタイル
pom.xml
<!-- 追記箇所(dependenciesタグ) -->

<!-- SpotBugs -->
<dependency>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>4.2.3</version>
</dependency>

<!-- Checkstyle -->
<dependency>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <version>3.1.2</version>
</dependency>

<!-- 追記箇所(build/pluginManagement/pluginsタグ) -->

<!-- SpotBugs -->
<plugin>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>4.2.3</version>
</plugin>

<!-- Checkstyle -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <version>3.1.2</version>
</plugin>

<!-- 追記箇所(reporting/pluginsタグ) -->

<!-- SpotBugs -->
<plugin>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>4.2.3</version>
</plugin>

<!-- Checkstyle -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <version>3.1.2</version>
</plugin>
SpotBugs/Checkstyleによる静的コード解析
% mvn site
[INFO] Scanning for projects...
...
[INFO] Generating "SpotBugs" report      --- spotbugs-maven-plugin:4.2.3:spotbugs
[INFO] Generating "Checkstyle" report    --- maven-checkstyle-plugin:3.1.2:checkstyle
...

SpotBugs.png

Checkstyle.png


用語集

用語 内容
スコープ(scope) 開発プロジェクトの作業範囲。
テストクラス(テストドライバ) テスト対象クラスのメインクラスを担うクラス。
テスティングフレームワーク(testing framework) テストケースの記述・実行・検証方法を定めた機能群。
パラメトリックテスト(parametric test) テストデータを微調整しながら複数回実行するテスト。
アサーション(assertion) テストケースをメインコードに記述できる仕組み。
コードカバレッジ(code coverage) テストにおいて、各テストケースによる命令文(=コード)の網羅(=カバー)率を定量的に測定する指標。
メトリクス(metrics) 品質や事象を数値化したもの。
技術的負債(technical debt) ソースコードの複雑さ。
リグレッションテスト(regression test) リファクタリング後に再度実施する単体テスト。
デグレード(degrade) リファクタリングの結果、バグが混入すること。
2
2
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
2
2