品質段階
プログラムの開発品質を判定する段階は、以下の通り。
段階 | 状態 | 内容 | 判定方法 |
---|---|---|---|
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 Code
のMavenプロジェクト
でJUnit
を導入する場合、
テストクラス
で利用するAssert
クラスの静的メソッドと、
テストメソッド
を表す@Test
アナテイションを定義するTest
クラスをインポートする必要がある。
サンプルコード
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;
}
}
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());
}
}
% 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 でない)であることを保証 |
処理の終了時 | 戻り値や実行結果が正常値であることを保証 |
常時 | インスタンスの内部状態が正常値であることを保証 |
サンプルコード
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ファイルを自動でダウンロードすることができる。
<!-- 追記箇所(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>
% 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ファイル
が自動生成される。
リファクタリングの手法
主なリファクタリング
手法は、以下の通り。
- 重複部分を
メソッド
で再定義メソッド
やクラス
の分割外部ライブラリ
や直近バージョンのJavaライブラリ
での代用コメント
の修正
静的コード解析(static code analysis)
静的コード解析
を行う主な開発ツールは、以下の通り。
ツール | 解析内容 |
---|---|
SpotBugs |
バグが生じる可能性のある記述 |
Checkstyle |
コーディングスタイル |
<!-- 追記箇所(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>
% 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
...
用語集
用語 | 内容 |
---|---|
スコープ(scope) | 開発プロジェクトの作業範囲。 |
テストクラス(テストドライバ) | テスト対象クラスのメインクラスを担うクラス。 |
テスティングフレームワーク(testing framework) | テストケースの記述・実行・検証方法を定めた機能群。 |
パラメトリックテスト(parametric test) | テストデータを微調整しながら複数回実行するテスト。 |
アサーション(assertion) | テストケースをメインコードに記述できる仕組み。 |
コードカバレッジ(code coverage) | テストにおいて、各テストケースによる命令文(=コード)の網羅(=カバー)率を定量的に測定する指標。 |
メトリクス(metrics) | 品質や事象を数値化したもの。 |
技術的負債(technical debt) | ソースコードの複雑さ。 |
リグレッションテスト(regression test) | リファクタリング後に再度実施する単体テスト。 |
デグレード(degrade) | リファクタリングの結果、バグが混入すること。 |