小ネタです。
TL;DR
以下のコードをヘッダ等に忍ばせておくことで、
お手軽に失敗時の詳細(左辺値・右辺値)を確認できるassertが書けます。
static inline void ASSERT_EQ_IMPL(const std::string &file, const int lineno,
const std::string &func,
const std::string &exp_symbol,
const auto &exp,
const std::string &act_symbol,
const auto &act) {
if (exp == act) {
return;
}
std::cerr << file << ": " << lineno << ": " << func << ": error: check "
<< exp_symbol << " == " << act_symbol << " has failed [" << exp
<< " != " << act << "]" << std::endl;
std::terminate();
}
# define ASSERT_EQ(exp, act) \
do { \
ASSERT_EQ_IMPL(__FILE__, __LINE__, __func__, #exp, exp, #act, act); \
} while (0)
背景
LeetCodeのような短いプログラムのテストを書くときや、
main loopも含めたライブラリのテストを書く時は、
GoogleTest等のtesting frameworkの導入が手間に感じるときがあります。
そのような場合、私は普通のassert
でテストを書くのですが、
下記のように一致しなかったことしかわからず、実際の値がどうなっていたのかがわからないため、
解析しづらいことが多いです。
そこで、失敗時の左辺値・右辺値を出力するようにしたASSERT_EQ
が、冒頭のものです。
後々GoogleTestに移行したときにちょっとでも楽になるようにI/FはGoogleTestに合わせています。
従来のassert
を使ったテストコード
int exp = 1;
int act = 2;
assert(exp == act);
従来のassert
の出力
test11: test.cc:87: int main(): Assertion `exp == act' failed.
今回のASSERT_EQ
の使用例
以下のように、整数値でもstd::string
でも比較できます。
テストコード(整数)
int exp = 1;
int act = 2;
ASSERT_EQ(exp, act);
出力(整数)
test.cc: 19: TestInt: error: check exp == act has failed [0 != 1]
テストコード(文字列)
std::string exp = "expected string";
std::string act = "actual string";
ASSERT_EQ(exp, act);
出力(文字列)
test.cc: 65: TestString: error: check exp == act has failed [expected string != actual string]
仮引数の型推論(C++14)なんか使えないんだけど?
最初の例は仮引数の型推論を使っているので、C++14でコンパイルする必要があります。
テンプレートを使えばC++11でも同じようなことが書けます。
template <class T1, class T2>
static inline void ASSERT_EQ_IMPL(const std::string &file, const int lineno,
const std::string &func, const std::string &exp_symbol,
const T1 &exp, const std::string &act_symbol, const T2 &act) {
if (exp == act) {
return;
}
std::cerr << file << ": " << lineno << ": " << func << ": error: check "
<< exp_symbol << " == " << act_symbol << " has failed [" << exp
<< " != " << act << "]" << std::endl;
std::terminate();
}
# define ASSERT_EQ(exp, act) \
do { \
ASSERT_EQ_IMPL(__FILE__, __LINE__, __func__, #exp, exp, #act, act); \
} while (0)
Scoped Enumの比較に使うとエラーになるんだけど?
型チェックが厳格なのでありがたいScoped Enumですが、iostreamによる出力ができないためそのままではエラーになります。
下記のサイトで紹介してくれている実装を使うことで、Scoped Enumも比較できるようになります(田原さん、ありがとうございます)。
scoped enumをお手軽に出力できるようにする | Theolizer®
実装
テストも含めた実装はこちらに置いています。