C++単体テストGoogle Testのメモ

環境

  • Ubuntu 20.04@WSL2

      ❯ lsb_release -a
      No LSB modules are available.
      Distributor ID: Ubuntu
      Description:    Ubuntu 20.04.3 LTS
      Release:        20.04
      Codename:       focal
    
  • C++ compiler: g++

      ❯ g++ --version
      g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
      Copyright (C) 2019 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions.  There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

やること

Google Testを使ったC++単体テスト。(初心者はビルドツールはBazelを使うことを推奨している)

If you’re using GoogleTest for the first time or need a refresher, we recommend this tutorial as a starting point.

Quickstart: Building with Bazel

Bazelのインストール

Bazelを直接インストールのではなく、Bazeliskというラッパーをインストールする。go install, npm installらへんでインストールするのが楽

npm install -g @bazel/bazelisk

bazelisk/README.md at master · bazelbuild/bazelisk

npmでインストール後は、 bazel baseliskシンボリックリンクが追加されていた

~/.nvm/versions/node/v15.3.0/bin ※PATHがすでに通っている
bazel -> ../lib/node_modules/@bazel/bazelisk/bazelisk.js ※インストール後追加されていた
bazelisk -> ../lib/node_modules/@bazel/bazelisk/bazelisk.js ※インストール後追加されていた

Bazel workspaceの用意

テストコードを追加するローカルリポジトリをBazel workspaceとする

  1. まず、このworkspace内で使用するBazelのバージョンを指定するために、リポジトリのroot直下に .bazelversion ファイルを用意する。

     cd ${workspaceとするディレクトリのパス}
     echo '5.1.0' > .bazelversion
    
  2. Google Testの依存を追加するために、WORKSPACEファイルを追加する。

     load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
    
     http_archive(
       name = "com_google_googletest",
       urls = ["https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip"],
       strip_prefix = "googletest-609281088cfefc76f9d0ce82e1ff6c30cc3591e5",
     )
    

これでGoogleTestの依存関係を追加できているはずなので、GoogleTestをインクルードしたテストコードをビルドすることができる。

テストコードを用意して、ビルド実行

  1. テストコード追加

     #include <gtest/gtest.h>
    
     // Demonstrate some basic assertions.
     TEST(HelloTest, BasicAssertions) {
       // Expect two strings not to be equal.
       EXPECT_STRNE("hello", "world");
       // Expect equality.
       EXPECT_EQ(7 * 6, 42);
     }
    
  2. BUILD ファイルを追加

     cc_test(
       name = "hello_test",
       size = "small",
       srcs = ["hello_test.cc"],
       deps = ["@com_google_googletest//:gtest_main"],
     )
    
  3. ビルド実行

    //以降は、BUILDファイルのWorkspaceのルートからの相対パス

     bazel test --test_output=all //:hello_test
     bazel test --test_output=all //${BUILDファイルのパス}:${name}
    

    Build Tutorial - C++

Googletestの書き方

Googletest Primer

テストを書く前に

用語

googletestはISTQBなどで定義される、一般的な用語の定義とは違うので、注意が必要

Meaning googletest term ISTQB term
grouping related tests Test Case Test Suite
Exercise a particular program path with specific input values and verify the results Test Test Case

Assertions

googletest assertionsはマクロ

Assertions Description
ASSERT_* generate fatal failures when they fail, and abort the current function
EXPECT_* generate nonfatal failures, which don’t abort the current function.

Assertions Reference

Assertions Reference

シンプルなテストを書く

フォーマット

  • 第一引数:the name of the test suite
  • 第二引数:the test’s name within the test suite

※どちらもC++で有効なidentiferで、_ は使えない

TEST(TestSuiteName, TestName) {
  ... test body ...
}

自分はこういう風にした

インクルードが面倒でよくわからなかったので、パッケージごとにBUILDファイル用意。パッケージのディレクトリ直下に必要なファイルをすべて用意。

  1. lib配下にパッケージのディレクトリを作成

     cd lib
     mdkir Math ※パッケージ名の例
    
  2. 問題を解いたあとに、スニペットにしておきたいと思ったものを、関数にしておく

     touch digit.hpp
    
  3. 2.の単体テストコードを用意

     touch digitTest.cpp
    
  4. 作成したパッケージのディレクトリ内にBUILDファイルを用意

     touch BUILD
    
     cc_test(
       name = "lib_math_test", ※任意の名前。実行する際に使う
       size = "small",
       srcs = ["digit.hpp","digitTest.cpp"], ※テストコードと、テストコードがインクルードするファイルを記載
       deps = ["@com_google_googletest//:gtest_main"],
     )
    
  5. テスト実行

     bazel test --test_output=all //lib/Math:lib_math_test ※Mathパッケージ配下のテストコードを実行する
    
  6. 完成したらVSCode用にスニペットにしておく

    snippet generator

ディレクトリ構成

lib
└── Math ※パッケージ
    ├── BUILD ※パッケージごとに用意
    ├── digit.h ※実装ファイル(今回はテンプレート関数だったので、実装もヘッダファイルで宣言。なのでヘッダファイルのみ)
    └── digitTest.cpp ※テストコード

テンプレート関数のテストを書く時に参考にした記事

テンプレートのヘッダを用意した場合の注意点。テンプレートはヘッダに宣言だけでなく実装も定義する。

C++ テンプレート ヘッダと実装を別にした時に起こること - Qiita

その他

Google C++ スタイルガイド 日本語全訳