C++における単体テスト
[第]49[課では]パフォーマンスの最適化について学びました。
[今]、[私たちは]ユニットテスト――[コードの][品質を保証するための重要なスキル]――を学びます。
[コードを]書いた[だけでテストしない]のは、[料理を作ったのに味見をしない]ようなものだ。
1. ユニットテスト[概要]
(1) 1.1 ユニットテストとは?
ユニットテスト(Unit Test)[とは、][最小のテスト可能単位](関数、クラス)[に対して行うテスト]のことです。
[強み]:
- [問題の早期発見]
- [リファクタリング時の保証]
- [ドキュメント化]コード[動作]
(2) 1.2 [テストフレームワークの選定]
| [フレームワーク] | [説明] |
|---|---|
| Google Test | Google[出品],[最]stream[行] |
| Catch2 | [単一]ヘッダーファイル、[シンプルで使いやすい] |
| Boost.Test | Boost[ライブラリの一部] |
| Doctest | [Catch2に似た]クラス、[より高速] |
[おすすめ]: Google Test([機能充実])[または] Catch2([シンプル])
2. Google Test[入門]
(1) 2.1 [インストール]Google Test
Linux:
sudo apt-get install libgtest-dev
Windows: [vcpkgを使用するか、ソースコードから]コンパイルします。
(2) 2.2 [最初のテスト]
例:[Google Testを使用して]加算をテストする関数 (難易度 ⭐)
▶ サンプル 2: codeexample (難易度 ⭐)
#include <gtest/gtest.h>
int add(int a, int b) {
return a + b;
}
TEST(AddTest, Positive) {
EXPECT_EQ(add(2, 3), 5);
}
TEST(AddTest, Negative) {
EXPECT_EQ(add(-2, -3), -5);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
[実行結果]:
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.
3. [アサーションマクロ]
(1) 3.1 ASSERT と EXPECT の違い
| [マクロ] | [失敗時の動作] |
|---|---|
ASSERT_* |
[終了][現在テスト中] |
EXPECT_* |
[続行][実行] |
[おすすめ]: [使用] EXPECT_*([さらに多くの失敗例を確認可能])
(2) 3.2 [よく使われるアサーション]
| [断言] | [説明] |
|---|---|
EXPECT_EQ(a, b) |
a == b |
EXPECT_NE(a, b) |
a ≠ b |
EXPECT_LT(a, b) |
a < b |
EXPECT_GT(a, b) |
a > b |
EXPECT_TRUE(cond) |
cond[真] |
EXPECT_FALSE(cond) |
cond[false] |
EXPECT_THROW(statement, exception_type) |
[期待]例外をスローする |
4. [試験治具](Test Fixture)
(1) 4.1 なぜ[治具]が必要なのでしょうか?
[問題]: [複数のテストで][同じ初期化コード]が必要。
[解決策]: [試験治具](継承 testing::Test)
(2) 4.2 例:[治具を用いたキューのテスト] (難易度 ⭐⭐)
#include <gtest/gtest.h>
#include queue
class QueueTest : public testing::Test {
protected:
void SetUp() override {
q.push(1);
q.push(2);
q.push(3);
}
void TearDown() override {
// ()
}
std::queueint q;
};
TEST_F(QueueTest, Size) {
EXPECT_EQ(q.size(), 3);
}
TEST_F(QueueTest, Front) {
EXPECT_EQ(q.front(), 1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
5. [テスト駆動開発](TDD)
(1) 5.1 TDDとは何か?
TDD(テスト駆動開発)とは、[まずテストを書き]、[その後]コードを書くことを指します。
stream[程]:
- [テストを作成]([失敗])
- [写]code([通過])
- [リファクタリング]
(2) 5.2 例:TDD[開発]stack (難易度 ⭐⭐⭐)
// 1. writetesting
TEST(StackTest, PushAndPop) {
Stackint s;
s.push(42);
EXPECT_EQ(s.pop(), 42);
}
// 2. write
template<typename T>
class Stack {
std::vectorT data;
public:
void push(const T& x) { data.push_back(x); }
T pop() { T x = data.back(); data.pop_back(); return x; }
};
6. 演習:[学生のテスト]クラス
▶ サンプル 1: [完全なテスト] (難易度 ⭐⭐⭐)
#include <gtest/gtest.h>
#include string
class Student {
std::string name;
int age;
public:
Student(const std::string& name, int age) : name(name), age(age) {}
std::string getName() const { return name; }
int getAge() const { return age; }
};
TEST(StudentTest, Constructor) {
Student s("...", 20);
EXPECT_EQ(s.getName(), "...");
EXPECT_EQ(s.getAge(), 20);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
❓ よくある質問
Q:ユニットテストで[すべての]バグを[発見できる]か? A:[できない]。ユニットテストでは、[既知の]タイプのエラーしか[発見できない]。[さらに、統合テスト]や[システムテスト]も必要となる。
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| ユニットテスト | [最小テスト単位] |
| Google Test | [最]先端の[C++][テストフレームワーク] |
| [断言マクロ] | EXPECT_* [和] ASSERT_* |
| [テスト治具] | [共有の初期化]code |
| TDD | [まずテストを書く],[それから]コードを書く |
📝 練習問題
-
**初心者(難易度 ⭐):[add(a,b) 関数] を[作成]し、[Google Test を使って] 3 [個]のテストケースを[作成]してください:[正の数の加算]、[負の数の加算]、[0 の加算]。
-
**中級(難易度 ⭐⭐):[isPalindrome関数を作成し]、[文字列が]回文であるかどうかを[判定]してください。[少なくとも]5つのテストケースを[作成し]、[正常]/[境界]/[特殊]な入力を[網羅]してください。
-
**上級(難易度 ⭐⭐⭐):[1つの]「[銀行口座]」クラス([預入/引出/残高照会などの]メソッドを含む)に対して、[完全な]ユニットテストを[作成する]。[通常の入出金]、[当座貸越の拒否]、[並行処理の安全性]([該当する場合])をテストする。
- ユニットテスト[検証]関数[またはモジュールの正しさ]
- [テストフレームワーク]:Google Test / Catch2 / doctest
- テストケース[3つの部分]:[準備]→[実行]→[アサーション]
- [テスト駆動開発](TDD):[まずテストを書いてから]コードを書く
- [カバレッジ測定によるテストの網羅性の評価]
[次のレッスン]:演習:[アドレス帳システム](#51)



