Visual StudioでGoogle Test

vs2022でUnitTest開発

※この記事にはプロモーションが含まれています。

スポンサーリンク
スポンサーリンク

はじめに

今回は、Visual Studio 2022を用いたGoogle Testの使用方法について、簡単に説明します。

Google Testの準備

Google Testの準備、テストプロジェクトの作成方法については、Visual Studio 2022でC++の無料Unit Test環境を構築の記事で紹介しています。こちらを参考に、用意してください。

アサーションについて

概要

関数的なアサーションを用いて結果を評価し、テストの合否を決定します。

アサーションには大きく分けて2種類の動作が存在し、以下の様にテストを実行します。XXXの部分は、複数種類が用意されています。基本的には、EXPECT_XXXを使用していれば、問題ないかと思います。

  • ASSERT_XXX
    評価が失敗した場合、そこでテストが終了します。これ以上テストを続行しても意味がない場合に使用します。
  • EXPECT_XXX
    評価が失敗した場合でも、致命的でない限り、次のテストに移ります。

アサーションの種類

アサーションには、評価する内容に合わせて、様々なバリエーションが用意されています。

主なアサーションを紹介します。

表では、EXPECT系を記載していますが、同じバリエーションのASSERT系アサーションがあります。

表記評価内容
EXPECT_TRUE(condition)condition==true
EXPECT_FALSE(condition)condition!=true
EXPECT_EQ(val1, val2)val1==val2
EXPECT_NE(val1, val2)val1!=val2
EXPECT_LE(val1, val2)val1<=val2
EXPECT_LT(val1, val2)val1<val2
EXPECT_GE(val1, val2)val1>=val2
EXPECT_GT(val1, val2)val1>val2
EXPECT_STREQ(s1, s2)(文字列として)s1==s2
EXPECT_STRNE(s1, s2)(文字列として)s1!=s2
EXPECT_STRCASEEQ(s1, s2)(大文字小文字区別無く文字列として)s1==s2
EXPECT_STRCASENE(s1, s2)(大文字小文字区別無く文字列として)s1!=s2
EXPECT_FLOAT_EQ(val1, val2)float型の精度範囲内でval1==val2
EXPECT_DOUBLE_EQ(val1, val2)double型の精度範囲内でval1==val2
EXPECT_NEAR(val1, val2, abs_error)絶対誤差abs_errorの範囲内でval1==val2
EXPECT_HRESULT_SUCCEEDED(expr)WindowsのHRESULT型がSUCCEEDED(expr)
EXPECT_HRESULT_FAILED(expr)WindowsのHRESULT型がFAILED(expr)
gtest.hから抜粋した主なアサーション一覧

テスト用サンプルコード

今回テストに用いる製品コードは、以下の通りとします。テストプロジェクトの作成方法等は、前回の記事を参照してください。

#pragma once

#include <string>
#include <vector>

class Product {
public:
	Product() = default;
	virtual ~Product() = default;
	static int pow(int a);
	std::string printValue(int value);
	int sumVector(const std::vector<int>& vec);
	bool isPositive(int value);
protected:
	bool isNegative(int value);
};
#include "Product.h"
#include <numeric>
int Product::pow(int a) {
	return a * a;
}
std::string Product::printValue(int value) {
	auto ret = this->isPositive(value) ? "P" : "N";
	return ret;
}
bool Product::isNegative(int value) {
	return (value < 0);
}
bool Product::isPositive(int value) {
	return (value > 0);
}
int Product::sumVector(const std::vector<int>& vec) {
	auto sum = std::accumulate(vec.begin(), vec.end(), 0);
	return sum;
}

テストの記述方法

基本的なテスト方法

基本的なテストの記述方法は、TESTマクロ内にアサーションを記述します。

TEST(A, B) {
  EXPECT_TRUE(true);
}

Bにはテスト名Aには各テストを束ねるテストスイート名をつけます。AおよびBには、有効なC++識別名を使用し、“_”(アンダースコア)は使用してはいけません(詳細はSimpleTests参照)。

#include "pch.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#pragma comment(lib,"gmock.lib")
#pragma comment(lib,"gmock_main.lib")

#include "Product.h"
#include "Product.cpp"

TEST(SamplePow, HandlesPositiveInput) {
	EXPECT_EQ(1, Product::pow(1));
	EXPECT_EQ(4, Product::pow(2));
	EXPECT_EQ(9, Product::pow(3));
}

TEST(Sample, printValue) {
	Product p;
	EXPECT_STREQ("P", p.printValue(2).c_str());
}

標準出力される結果はこんな感じです。

[==========] Running 2 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 1 test from SamplePow
[ RUN      ] SamplePow.HandlesPositiveInput
[       OK ] SamplePow.HandlesPositiveInput (0 ms)
[----------] 1 test from SamplePow (0 ms total)

[----------] 1 test from Sample
[ RUN      ] Sample.printValue
[       OK ] Sample.printValue (0 ms)
[----------] 1 test from Sample (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 2 test suites ran. (2 ms total)
[  PASSED  ] 2 tests.

Visual Studioのテストエクスプローラーだと、ツリー構造のAが親ノード、Bがその子ノードとして表示され、B単位でテストの合否が算出されます。

評価するリソースを効率よく使用するテスト方法

同じ前処理を持つ複数のテストを、何度も記述するのは面倒なので、まとめて定義できるフィクスチャという機能が実装されています。

この機能を利用するには、::testing::Testクラスの派生クラスを作成し、TEST_Fマクロにテストを実装します。SetUp()・TearDown()関数を用意することで、リソースの確保・解放を担当させます(CASEに注意です、特にSetUp)。実行順は、コンストラクタ→SetUp→テスト→TearDown→デストラクタ、です。

慣例ではテストするクラス名+”Test”という派生クラス名とし、TEST_Fにそのクラス名とテスト名を指定します。

class ProductTest : public ::testing::Test {
protected:
    virtual void SetUp() {
        v1 = { 1,2,3 };
        v2 = {};
        v3 = { -1 };
    }

    virtual void TearDown() {}

    std::vector<int> v1;
    std::vector<int> v2;
    std::vector<int> v3;
};

TEST_F(ProductTest, Sum) {
    Product p;
    EXPECT_EQ(6, p.sumVector(v1));
    EXPECT_EQ(0, p.sumVector(v2));
    EXPECT_EQ(-1, p.sumVector(v3));
}
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from ProductTest
[ RUN      ] ProductTest.Sum
[       OK ] ProductTest.Sum (0 ms)
[----------] 1 test from ProductTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (2 ms total)
[  PASSED  ] 1 test.

フィクスチャを上手く活用することで、テストコードを減らすことができるようになります。

Protect関数を評価するテスト方法

これまでの方法は、製品コードのPublicなメンバにしかアクセスできません。かといって、製品コード内でifdefで切り分けたりする訳にもいきません。

製品コード内のProtect関数をテストしたい場合、自製クラスと::testing::Testクラスを継承したテストクラスを作成します。実装は不要です。

class ProductTest : public Product, public ::testing::Test {

};
TEST_F(ProductTest, NegativeCheckZero) {
	EXPECT_FALSE(isNegative(0));
}

これにより、テストクラスメンバとして、隠匿メンバにアクセスできるようになり、評価することが可能です。

評価値をまとめて指定するテスト方法

境界値確認の様に、インプットだけを変更したテストを延々記述するのは面倒です。そのような場合はは、TestWithParamを利用したテストコードを実装することで、パラメーターのみを変化させてテストを実施することができます。

下記の例では、境界値(-1,0,1)とその回答(true/false)をパラメータとして渡しています。

class ProductFixture : public ::testing::TestWithParam<std::tuple<int, bool>> {

};

TEST_P(ProductFixture, test) {
	std::tuple<int, bool> param = GetParam();

	auto answer = std::get<1>(param);
	auto value = std::get<0>(param);

	Product p;
	auto test = p.isPositive(value);

	EXPECT_EQ(answer, test);
}

INSTANTIATE_TEST_SUITE_P(isPositiveTest, ProductFixture, ::testing::Values(
	std::make_tuple(0, false),
	std::make_tuple(1, true),
	std::make_tuple(-1, false)
));
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from isPositiveTest/ProductFixture
[ RUN      ] isPositiveTest/ProductFixture.test/0
[       OK ] isPositiveTest/ProductFixture.test/0 (0 ms)
[ RUN      ] isPositiveTest/ProductFixture.test/1
[       OK ] isPositiveTest/ProductFixture.test/1 (0 ms)
[ RUN      ] isPositiveTest/ProductFixture.test/2
[       OK ] isPositiveTest/ProductFixture.test/2 (0 ms)
[----------] 3 tests from isPositiveTest/ProductFixture (1 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (2 ms total)
[  PASSED  ] 3 tests.

まとめ

今回は、Google Testの実施方法について、Visual Studioの環境を用いて説明しました。

デグレード確認に重宝しますので、どんどん書いていきましょう。

次回は、テストがより効率的に行えるモックの使い方について記載する予定です。

以上、Visual StudioでGoogle Testの紹介でした。

コメント