目的
- 作成したC++プログラムのテストを行う
- GoogleTestの練習
- 共有ライブラリを使う練習
環境
- M1 MacBook Air Sonoma 14.7.1
- Homebrew 4.4.13
- GCC 14.2.0_1
- cmake 3.31.3
- GoogleTest stable 1.15.2
手順
GoogleTestのinstall
Homebrewでinstallする。GitHubからクローンしてcmakeでビルドする方法もあり。
$ brew install googletest
installされたか確認。
$ ls -l /opt/homebrew/include | grep googletest
lrwxr-xr-x 1 user admin 41 Dec 30 12:32 gmock -> ../Cellar/googletest/1.15.2/include/gmock
lrwxr-xr-x 1 user admin 46 Dec 30 12:32 googlemock -> ../Cellar/googletest/1.15.2/include/googlemock
lrwxr-xr-x 1 user admin 46 Dec 30 12:32 googletest -> ../Cellar/googletest/1.15.2/include/googletest
lrwxr-xr-x 1 user admin 41 Dec 30 12:32 gtest -> ../Cellar/googletest/1.15.2/include/gtest
基本的なTestの実行方法
- TEST() マクロを使って、テスト関数の定義と命名を行う
- テスト関数の中で、任意の有効なC++ステートメントをincludeし、GoogleTestのアサーションで動作を確認する
- テスト結果は、アサーションによって決まる。どれか一つでもアサーションが失敗すると、テスト全体が止まる
TEST() マクロの基本的な使い方は、以下。
TEST(TestSuiteName, TestName) {
... test body ...
}
TestSuiteName: あるテストのまとまりの名前
TestName: そのテストの名前
GoogleTestは、関連するテストスイートごとにグループ化するため、関連するテストは同じテストスイートにする。使用可能アサーションは、下のリンクに一覧されている。
GoogleTest アサーションまとめ (引数と使用例付き)
アサーション | 説明 | 引数 | 使用例 | 便利な場面 |
---|---|---|---|---|
EXPECT_TRUE |
条件が真であることを検証する。 |
condition : 真偽値を返す式 |
EXPECT_TRUE(x > 5); |
条件式の評価結果がテストの成否を決定する場合。 |
EXPECT_FALSE |
条件が偽であることを検証する。 |
condition : 真偽値を返す式 |
EXPECT_FALSE(y == 0); |
条件式の評価結果がテストの成否を決定する場合。 |
EXPECT_EQ |
2つの値が等しいことを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_EQ(actual, expected); |
変数の値が期待値と等しいかどうかを比較する場合。 |
EXPECT_NE |
2つの値が等しくないことを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_NE(result, error_code); |
変数の値が特定のエラーコードと異なるかどうかを比較する場合。 |
EXPECT_LT |
ある値が別の値より小さいことを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_LT(a, b); |
変数の大小関係(より小さい)を比較する場合。 |
EXPECT_LE |
ある値が別の値以下であることを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_LE(count, max_count); |
変数の大小関係(以下)を比較する場合。 |
EXPECT_GT |
ある値が別の値より大きいことを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_GT(size, 0); |
変数の大小関係(より大きい)を比較する場合。 |
EXPECT_GE |
ある値が別の値以上であることを検証する。 |
val1 , val2 : 比較する2つの値 |
EXPECT_GE(age, 18); |
変数の大小関係(以上)を比較する場合。 |
EXPECT_STREQ |
2つのC文字列の内容が同じであることを検証する。 |
str1 , str2 : 比較するC文字列 |
EXPECT_STREQ("hello", actual_string); |
C文字列の内容を比較する場合。 |
EXPECT_STRNE |
2つのC文字列の内容が異なることを検証する。 |
str1 , str2 : 比較するC文字列 |
EXPECT_STRNE("goodbye", input); |
C文字列の内容が特定の値と異なるかどうかを比較する場合。 |
補足:
-
ASSERT_
で始まるアサーションは、失敗した場合にテスト関数を即座に中断する。EXPECT_
で始まるアサーションは、失敗してもテスト関数は続行される - これらのアサーションは、単体テストにおいて、コードの挙動が期待通りかどうかを検証するために使用される
- 引数の型が一致しない場合、コンパイルエラーが発生する
- 浮動小数点数の比較には、
EXPECT_FLOAT_EQ
、EXPECT_DOUBLE_EQ
、EXPECT_NEAR
など、より適切なアサーションが用意されている。これは、浮動小数点数の演算誤差を考慮するため
GoogleTestの公式サンプルを試す
このリンクにSampleが乗っていたので、試しに実行する。下のコードは、Googleのサンプルコードを一部改変したものである。BSDライセンスなので、本記事への記載は問題ないと思われる。オリジナルテストコードの著作権は、Google Inc.に帰属する。
sample1.hpp
#pragma once
// 階乗の計算
int Factorial(int n);
// 素数判定
bool IsPrime(int n);
sample1.cpp
#include "sample1.hpp"
// 階乗の計算
int Factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
// 素数判定
bool IsPrime(int n)
{
// 1を除外
if (n <= 1)
return false;
// 偶数のうち2だけ素数
if (n % 2 == 0)
return n == 2;
// 3以降
for (int i = 3;; i += 2)
{
// nの平方根だけの回数実行
if (i > n / i)
break;
// 合致する値があれば、素数でない
if (n % i == 0)
return false;
}
return true;
}
階乗の計算と素数判定のシンプルなプログラム。次に、このプログラムに対するテストコードを作成する。(TDDがお好きな方は、こちらから先に書いても良いかもしれない)。
#include "sample1.hpp"
#include <limits.h>
#include <gtest/gtest.h>
TEST(FactorialTest, Negative)
{
// This test is named "Negative", and belongs to the "FactorialTest"
// test case.
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
}
// Tests factorial of 0.
TEST(FactorialTest, Zero) { EXPECT_EQ(1, Factorial(0)); }
// Tests factorial of positive numbers.
TEST(FactorialTest, Positive)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// Tests IsPrime()
// Tests negative input.
TEST(IsPrimeTest, Negative)
{
// This test belongs to the IsPrimeTest test case.
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
}
// Tests some trivial cases.
TEST(IsPrimeTest, Trivial)
{
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
}
// Tests positive input.
TEST(IsPrimeTest, Positive)
{
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
main.cpp
#include <gtest/gtest.h>
#include "sample1.hpp"
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
このmain関数で、テストを実行する。最終的なファイル構成は、以下のようになった。
├── CMakeLists.txt
├── main.cpp
├── sample1.cpp
├── sample1.hpp
└── sample1_unittest.cpp
CMakeLists.txtの作成
# CMakeのバージョン設定
cmake_minimum_required(VERSION 3.13)
# C++のバージョン設定
set(CMAKE_CXX_STANDARD 14)
# プロジェクト名
project(gtest_practice)
# googletestをひもづける
find_package(GTest REQUIRED)
# テスト用の実行ファイルを作成
add_executable(
${PROJECT_NAME}_tests
main.cpp
sample1.cpp
sample1_unittest.cpp
)
# googletestをリンク
target_link_libraries(
${PROJECT_NAME}_tests
PRIVATE
GTest::gtest
)
cmake -S . -B build
cmake --build build
結果
$ ./gtest_practice_tests
[==========] Running 6 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 3 tests from FactorialTest
[ RUN ] FactorialTest.Negative
[ OK ] FactorialTest.Negative (0 ms)
[ RUN ] FactorialTest.Zero
[ OK ] FactorialTest.Zero (0 ms)
[ RUN ] FactorialTest.Positive
[ OK ] FactorialTest.Positive (0 ms)
[----------] 3 tests from FactorialTest (0 ms total)
[----------] 3 tests from IsPrimeTest
[ RUN ] IsPrimeTest.Negative
[ OK ] IsPrimeTest.Negative (0 ms)
[ RUN ] IsPrimeTest.Trivial
[ OK ] IsPrimeTest.Trivial (0 ms)
[ RUN ] IsPrimeTest.Positive
[ OK ] IsPrimeTest.Positive (0 ms)
[----------] 3 tests from IsPrimeTest (0 ms total)
[----------] Global test environment tear-down
[==========] 6 tests from 2 test suites ran. (0 ms total)
[ PASSED ] 6 tests.
その他の調べ物
C言語標準ライブラリ・limits.h
#include <limits.h>
ヘッダー では、標準的な整数型のさまざまな限界値やパラメータに展開するマクロがいくつか定義されています。標準的な整数型のさまざまな制限やパラメータに展開するいくつかのマクロを定義しています。
フリースタンディング環境で使える標準ライブラリの1つです。
色々な変数の限界値や大きさをまとめたマクロ。標準ライブラリの一つ。
参考