*テスト駆動開発(TDD)**は、テストを最初に書いてからコードを書くソフトウェア開発の手法です。
TDDを行うことで、コードの品質を高め、バグを早期に発見することができます。Rustでは、テストフレームワークが標準で組み込まれており、効率的にテストを行うことができます。
この記事では、Rustのテスト機能を使った基本的なテストの書き方と、TDDの実践手法を学びます。
Rustにおけるテストの基本
Rustには、コードの品質を保つためのテストフレームワークが標準で備わっています。
#[test]
属性を使って簡単にテストを作成し、cargo test
コマンドでテストを実行できます。
基本的なテストの書き方
まずは、基本的なテストの書き方を見てみましょう。
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5); // add(2, 3)の結果が5であることを確認
}
}
ポイント:
#[cfg(test)]
を使うことで、テストコードが通常のコンパイル時に含まれないようにします。#[test]
属性を関数に付けることで、その関数がテストであることを示します。assert_eq!
マクロを使って、期待する結果と実際の結果が一致しているかを確認します。
テストの実行
テストを実行するには、cargo test
コマンドを使用します。
$ cargo test
cargo test
コマンドは、プロジェクト全体のテストを自動で実行し、結果を出力します。
テスト駆動開発(TDD)の基本
**テスト駆動開発(TDD)**は、以下の3つのステップで進行します。
- Red: 失敗するテストを書く。
- Green: テストが成功するように最小限のコードを書いてテストを通す。
- Refactor: コードをリファクタリングして、クリーンで最適な形に改善する。
TDDの実践手順
次に、TDDの実践手順をRustのコード例で示します。
ステップ1: 失敗するテストを書く(Red)
まず、multiply
という関数が存在するという前提で、失敗するテストを書きます。
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multiply() {
assert_eq!(multiply(2, 3), 6); // multiply関数はまだ存在しないためエラー
}
}
この段階では、multiply
関数が定義されていないので、テストは失敗します。
コードを書いてテストを通す(Green)
次に、テストが成功するように multiply
関数を最小限のコードで実装します。
fn multiply(a: i32, b: i32) -> i32 {
a * b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multiply() {
assert_eq!(multiply(2, 3), 6); // テストが成功する
}
}
multiply
関数を実装することで、テストが成功します。
ステップ3: コードをリファクタリングする(Refactor)
最後に、必要に応じてコードをリファクタリングし、テストが成功したままコードの質を向上させます。
今回の例では、特にリファクタリングの必要はないため、そのまま完了です。
より高度なテスト手法
パニックテスト
Rustでは、パニックが発生するかどうかをテストすることも可能です。
#[should_panic]
属性を使って、パニックが発生することを期待するテストを書きます。
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("ゼロで割ることはできません");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "ゼロで割ることはできません")]
fn test_divide_by_zero() {
divide(10, 0); // ここでパニックが発生することを期待
}
}
ポイント:
#[should_panic]
属性を使って、特定の条件でパニックが発生することをテストします。expected
パラメータで、パニック時のエラーメッセージを指定することができます。
テストのフィルタリング
cargo test
コマンドでは、特定のテストのみを実行することが可能です。
テストの名前をフィルタとして指定できます。
$ cargo test test_multiply
このコマンドは、test_multiply
という名前を含むテストだけを実行します。
インテグレーションテスト
Rustでは、モジュールやパッケージ間の相互動作を確認するために、インテグレーションテストを行うことができます。
インテグレーションテストは、tests
ディレクトリに配置されるファイルで行います。
ディレクトリ構造
my_project/
├── src/
│ └── lib.rs
├── tests/
│ └── integration_test.rs
use my_project;
#[test]
fn test_integration() {
assert_eq!(my_project::add(2, 3), 5);
}
ポイント:
- インテグレーションテストは、プロジェクト全体をテストするために使われ、
tests
ディレクトリに配置されます。 - モジュール間の連携や、外部からの関数呼び出しをテストします。
練習問題
練習問題1: TDDを使って関数を実装
以下のステップに従って、TDDを使って subtract
関数を実装してください。
subtract(a, b)
関数が存在するという前提で、失敗するテストを書きます。- テストを通すために、最小限のコードで
subtract
関数を実装します。 - コードをリファクタリングし、テストを成功させたまま改善します。
まとめ
今回の記事では、Rustのテストフレームワークとテスト駆動開発(TDD)の基本について学びました。
TDDを実践することで、テストがコードの品質を向上させ、バグの発見を容易にします。
Rustの標準的なテストツールを活用し、プロジェクトのコードが常に正確であることを保証しましょう。