用語集 · テストの基礎
ユニットテスト(単体テスト)
英語: Unit test
別名: 単体テスト, unit tests, unit testing, ユニットテストとは
定義
関数やクラスなどの小さなコード単位を、データベース、ネットワーク、ほかのサービスから切り離して確認する自動テストです。
ユニットテストは、関数、メソッド、クラスといった小さなコードの単位を実行し、与えられた入力に対する出力や振る舞いを確認するテストです。本物のデータベース、HTTP 呼び出し、ファイルシステムを使わずにメモリ上で動くため、数ミリ秒で終わり、失敗する理由はただ 1 つに絞られます。ユニットテストはテストピラミッドの土台を形成し、通常は開発者がコードと一緒に書きます。
例(JUnit 5、Java 21)
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class DiscountCalculatorTest {
private final DiscountCalculator calculator = new DiscountCalculator();
@ParameterizedTest
@CsvSource({ "99.99, 0.00", "100.00, 10.00", "250.00, 25.00" })
void appliesTenPercentFromOneHundred(double total, double expected) {
assertEquals(expected, calculator.discountFor(total), 0.001);
}
@Test
void rejectsNegativeTotals() {
assertThrows(IllegalArgumentException.class, () -> calculator.discountFor(-1));
}
}
パラメーター化テストには junit-jupiter-params アーティファクトが必要で、これは junit-jupiter の集約依存関係に含まれています。(実際に金額を扱うコードでは double ではなく BigDecimal を使うべきですが、ここではシンプルにしています。)
良いユニットテストの条件
- 高速で決定的。 スリープ、時計、乱数、ネットワークを使いません。
LocalDate.now()を呼ぶ代わりに、固定のClockを注入します。 - 独立している。 テストを遅くしたり予測不能にしたりする依存関係は、テストダブルに置き換えます。
- 1 テストにつき 1 つの振る舞い。 その振る舞いを表す名前を付ければ、失敗したときに理由が一目でわかります。
- 実装ではなく振る舞いをテストする。 private メソッドではなく、出力や観測可能な効果に対してアサーションすれば、リファクタリングでテストが壊れません。
ユニットテストと結合テストの違い
ユニットテストは、ほかのすべてを置き換えたうえで自分のロジックを確認します。結合テストは、本物のコンポーネント同士が連携して動くことを確認します。たとえば本物のデータベースに対するリポジトリ、本物の(またはコンテナ化された)サービスに対するクライアントです。ユニットテストはロジックの誤りを安価に検出し、結合テストは、ユニットテストではモックで隠れてしまう配線、設定、シリアライズ、クエリの誤りを検出します。ユニットテストだけのスイートは、アプリケーションが起動すらしない状態でもすべてグリーンになりえます。
「ユニット」の定義
普遍的な答えはありません。1 つのクラスをユニットとみなし、協調するオブジェクトをすべてモックにするチームもあります。1 つの振る舞いを実装する少数のクラスのまとまりをユニットとみなし、プロセスの境界をまたぐものだけを置き換えるチームもあります。後者のスタイルのほうが、リファクタリングに対して壊れにくいテストになる傾向があります。