XcodeでGoogle Test (gtest) を使えるようにセットアップする

Xcodeにはユニットテストを実行する機能があります。Xcodeから実行できるユニットテスト機能はXCTestフレームワークのチェック関数を使って、クラスやメソッドの戻り値が期待値通りになっているかをチェックします。チェック結果はXcodeの中でリスト表示されます。

XCTestはAppleプラットフォーム専用です。クロスプラットフォーム対応のライブラリを作っているときなどには、XCTestを使うよりも、クロスプラットフォーム対応のテストフレームワークを使用して、各プラットフォーム上で直接ユニットテストを実行できることが重要です。

クロスプラットフォーム対応のテストフレームワークの中にGoogle製のGoogle Test (gtest) があります。C++のテストフレームワークでmacOSやWindowsなど複数のプラットフォームに対応しています。

この記事では、 gtest をmacOS上でXcodeと組み合わせて使えるようにし、XCTestを使ったユニットテストと一緒にテストできるようにする方法を解説します。

準備

この記事では、gtest をソースからビルドして使う方法を解説します。gtest のビルドには CMake が必要です。まず、 CMake をインストールしてください。インストール方法については、次の記事で解説しています。

[clink url=”https://www.rk-k.com/archives/3907″]

gtest をビルドする

gtest のコードはGitHubにありますので、GitHubからクローンするかダウンロードしてください。

GitHub : google/googletest

CMakeの設定変更

CMakeはv3.19.2でApple Silicon、Xcode 12の新ビルドシステムに対応しました。古いバージョンを使っている方はアップデートしましょう。

gtestはCMakeで、Xcodeのプロジェクトを生成して、Xcodeを使ってビルドします。しかし、この記事を執筆している時点では、生成されるプロジェクトの設定に問題があり、ビルドが通りません。

生成されるプロジェクトでは、C11が有効になっていません。gtestをビルドするにはC11を有効化する必要があります。

CMakeLists.txtを開き、以下を追加します。Appleプラットフォーム上のときは、C++11を有効化するという設定です。

if (APPLE)
	SET(CMAKE_CXX_STANDARD 11)
	SET(CMAKE_CXX_STANDARD_REQUIRED on)
endif()

追加する場所は、enable_testing()という行の上くらいが良いと思います。編集した後のファイルは次のようになります。

# Note: CMake support is community-based. The maintainers do not use CMake
# internally.

cmake_minimum_required(VERSION 2.8.8)

if (POLICY CMP0048)
  cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)

project(googletest-distribution)
set(GOOGLETEST_VERSION 1.10.0)

if (CMAKE_VERSION VERSION_GREATER "3.0.2")
  if(NOT CYGWIN AND NOT MSYS)
    set(CMAKE_CXX_EXTENSIONS OFF)
  endif()
endif()

if (APPLE)
	SET(CMAKE_CXX_STANDARD 17)
	SET(CMAKE_CXX_STANDARD_REQUIRED on)
endif()

enable_testing()

include(CMakeDependentOption)
include(GNUInstallDirs)

#Note that googlemock target already builds googletest
option(BUILD_GMOCK "Builds the googlemock subproject" ON)
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)

if(BUILD_GMOCK)
  add_subdirectory( googlemock )
else()
  add_subdirectory( googletest )
endif()

プロジェクトの生成

ビルドするために必要なプロジェクトの生成などを行います。次のようにクローンしたフォルダの直下にmybuildというフォルダを作ります。

GoogleTest
├── BUILD.bazel
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── WORKSPACE
├── appveyor.yml
├── ci
├── googlemock
├── googletest
├── library.json
├── mybuild
└── platformio.ini

ターミナルでmybuildに移動します。

$ cd GoogleTest/mybuild

次のようにcmakeを使って、プロジェクトを生成します。指定しているオプションは、以下の通りです。適時変更してください。

  • pthreadを有効化
  • インストール先をusr/local/gtestに設定
  • Xcodeのプロジェクトを生成する
$ cmake -DGTEST_HAS_PTHREAD=1 -G Xcode -DCMAKE_INSTALL_PREFIX=/usr/local/gtest ../

Universal Binaryの設定

M1 Macにも対応できるようにUniversal Binaryを出力するための設定を行います。

生成された googletest-distribution.xcodeproj を開きます。プロジェクトのオプションを開き、以下のように設定します。全ターゲットの設定を変更するか、ターゲット単位の設定を削除して、プロジェクト設定を使うかはどちらでも構いません。

Architectures : Standard Architectures (Apple Silicon, Intel) – $(ARCHS_STANDARD)

Base SDK : macOS

C++11が有効化されているか確認する

プロジェクトの設定でC++11が有効化されていることを確認します。生成された`googletest-distribution.xcodeproj`を開きます。

プロジェクトのオプションを開き、gmockなどのターゲットのオプションを表示します。Build SettingsタブのOther C++ Flagsに`’-std=c++11’`があるかを確認します。

ビルドとインストール

xcodebuildを使ってビルドします。XcodeでGUIでビルドしても良いのですが、ここではターミナルで行います。また、インストール先を/usr/local/gtestに指定したので、書き込むためには管理者権限が必要です。そのため、sudo付きで実行します。

$ sudo xcodebuild -target install build

しばらくして、ビルドが成功すると、/usr/local/gtestにインストールされます。

/usr/local/gtest
├── include
│   ├── gmock
│   └── gtest
└── lib
    ├── cmake
    ├── libgmock_maind.a
    ├── libgmockd.a
    ├── libgtest_maind.a
    ├── libgtestd.a
    └── pkgconfig

Xcodeとgtestを連携させる

Xcodeのテストナビゲータに表示させるには、XCTestを使う必要があります。gtestもXCTestのテストケースと同じように表示させるには一工夫必要です。その一工夫を実装してくれたコードがOSSで公開されています。

mattstevens/xcode-googletest: Google Test integration with Xcode

プロジェクトに必要ファイルを追加する

使用するのは、Bundle/GoogleTests.mです。このファイルをユニットテストバンドルのプロジェクトに追加します。

次に、ユニットテストバンドルのターゲットの設定で、「Build Settings」を開き、「Header Search Paths」と「Library Search Paths」に、インストールしたgtestのディレクトリを追加します。

以下の設定を書きます。

  • Header Search Paths : /usr/local/gtest/include
  • Library Search Paths : /usr/local/gtest/lib

ライブラリをリンクさせます。ユニットテストバンドルのターゲットに以下のライブラリを追加します。

  • libgtestd.a

プロジェクトに登録するのでも良いのですが、/usr/local/gtest/lib以下なので、少し面倒なら、次のようにOther Linker Flagsにオプション追加でも構いません。

  • Other Linker Flags : -lgtestd

テストを書く

確認するために簡単なテストコードを書いてみます。まずは、テスト対象のクラスを実装します。

// Counter.hpp
#ifndef COUNTER_HPP
#define COUNTER_HPP

class Counter {
private:
    int value_;
    
public:
    Counter();
    virtual ~Counter();
    
    void reset();
    void increment(int step = 1);
    void decrement(int step = 1);
    int value() const;
};

#endif /* COUNTER_HPP */
// Counter.cpp
#include "Counter.hpp"

Counter::Counter() : value_(0)
{
    
}

Counter::~Counter()
{
    
}

void Counter::reset()
{
    value_ = 0;
}

void Counter::increment(int step)
{
    value_ += step;
}

void Counter::decrement(int step)
{
    value_ -= step;
}

int Counter::value() const
{
    return value_;
}

テストケースを実装します。例えば、次のようなコードです。

// CounterTests.cpp
#include <gtest/gtest.h>
#include "Counter.hpp"

TEST(CounterTests, Initialize)
{
    Counter c;
    ASSERT_EQ(0, c.value());
}

TEST(CounterTests, Increment)
{
    Counter c;
    
    c.increment();
    ASSERT_EQ(1, c.value());
    
    c.increment(10);
    ASSERT_EQ(11, c.value());
}

TEST(CounterTests, Decrement)
{
    Counter c;
    
    c.decrement();
    ASSERT_EQ(-1, c.value());
    
    c.decrement(10);
    ASSERT_EQ(-11, c.value());
}

TEST(CounterTests, Reset)
{
    Counter c;
    
    c.increment();
    ASSERT_EQ(1, c.value());
    
    c.reset();
    ASSERT_EQ(0, c.value());
}

Xcodeでユニットテストを実行すると、テストナビゲータにXCTestで書いたテストと同様に項目が追加されて、テスト結果も表示されます。

著書紹介

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

目次