C++: ユニットテスト

最終更新:2026-08-31

レッスン49では、パフォーマンス最適化について学びました。

ここでは、ユニットテストについて学びます。

テスト可能なコードを書くことは、保守性の鍵です。


1. ユニットテストの基本

(1) 1.1 ユニットテストとは?

ユニットテスト(Unit Test)は最小単位(関数、クラス)のテストです。

利点:


(2) 1.2 テスティングフレームワーク

フレームワーク 説明
Google Test Google製、標準的
Catch2 ヘッダオンリ、使いやすい
Boost.Test Boostの一部
Doctest Catch2に似ている、高速

推奨: Google Test(標準的)または Catch2(手軽)




2. Google Test

(1) 2.1 Google Testのインストール

Linux:

BASH
sudo apt-get install libgtest-dev

Windows: vcpkgまたは手動コンパイル。


(2) 2.2 基本的なテスト

▶ サンプル 2:コード例(難易度 ⭐)

TEXT 📖 参照専用
#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();
}

出力:

TEXT 📖 参照専用
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.



3. アサーションマクロ

(1) 3.1 ASSERT vs 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) 条件が真
EXPECT_FALSE(cond) 条件が偽
EXPECT_THROW(statement, exception_type) 例外を投げる



4. テストフィクスチャ

(1) 4.1 なぜ必要?

問題: テスト間で共通のコードがある。

解決策: テストフィクスチャ(testing::Test を継承)


(2) 4.2 例:テストフィクスチャでキューをテスト(難易度 ⭐⭐)

CPP
#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::queue<int> 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(Test-Driven Development)はテストを先に書き、コードを後に書く手法です。

サイクル:

  1. テストを書く(失敗)
  2. コードを書く(成功)
  3. リファクタリング

(2) 5.2 例:TDDでスタックを実装(難易度 ⭐⭐⭐)

TEXT 📖 参照専用
// 1. テストを書く
TEST(StackTest, PushAndPop) {
    Stack<int> s;
    s.push(42);
    EXPECT_EQ(s.pop(), 42);
}

// 2. 最小限の実装
template<typename T>
class Stack {
    std::vector<T> data;
public:
    void push(const T& x) { data.push_back(x); }
    T pop() { T x = data.back(); data.pop_back(); return x; }
};



6. 実践:クラスのテスト

▶ サンプル 1:完全なテスト例(難易度 ⭐⭐⭐)

CPP
#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();
}
▶ 試してみよう

▶ サンプル 3:エッジケースのテスト(難易度 ⭐⭐)

CPP
#include <gtest/gtest.h>

int divide(int a, int b) {
    if (b == 0) {
        throw std::invalid_argument("ゼロ除算");
    }
    return a / b;
}

TEST(DivideTest, NormalCase) {
    EXPECT_EQ(divide(10, 2), 5);
    EXPECT_EQ(divide(9, 3), 3);
}

TEST(DivideTest, EdgeCases) {
    EXPECT_EQ(divide(0, 5), 0);      // 分子がゼロ
    EXPECT_EQ(divide(5, 5), 1);      // 同じ値
    EXPECT_EQ(divide(-10, 2), -5);   // 分子が負
}

TEST(DivideTest, ExceptionOnZero) {
    EXPECT_THROW(divide(10, 0), std::invalid_argument);
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
[==========] Running 3 tests from 1 test suite.
[ PASSED  ] 3 tests.

❓ よくある質問

Q:ユニットテストでバグを防げる? A:いいえ。ユニットテストは特定の種類のエラーを防げます。統合テスト、システムテストも必要。


Q:テストコードと本番コードの比率は? A:プロジェクトによりますが、テストコードの方が長いことも。


Q:プライベート関数をテストする? A:- 1. パブリックインターフェースをテスト(推奨)

    1. FRIEND_TESTマクロでアクセス
    1. リファクタリングして関数を別クラスに抽出

📖 まとめ

トピック 要点
ユニットテスト 最小単位のテスト
Google Test 標準的なC++テスティング
アサーションマクロ EXPECT_* と ASSERT_*
テストフィクスチャ 共通コードの整理
TDD テストを先に書き、コードを後に書く

📝 練習問題

  1. 初級(難易度 ⭐): add(a, b) 関数を書き、Google Testで3つのテストケースを書く:正の数、負の数、ゼロ。

  2. 中級(難易度 ⭐⭐): isPalindrome 関数(文字列が回文か判定)を書く。5つのテストケース:true、false、空文字、大文字小文字、数字入力。

  3. 上級(難易度 ⭐⭐⭐): 「銀行口座」クラス(deposit/withdraw/getBalance)を書き、ユニットテストを作成。正常、異常、並行性(オプション)をテスト。



次のレッスン:プロジェクト:住所録システム(#51)

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%