課題解決Tips

【C++】 gMockのEXPECT_CALLでエラーが出た時

salmoncode
記事内に商品プロモーション(Amazonアソシエイト含む)を含む場合があります。

こんにちは。エンジニアのサーモンです。

今回はgMockでハマったので、備忘録として記事を書きます。

gMockとはGoogle製のモックフレームワークで、同じくGoogle製のC++テストツールであるGoogleTestと合わせて使います。

gMockを使うことで、テスト対象が依存先の関数を呼び出したかチェックすることができます。
また、モックに特定の値を返させることで、関数の返り値によるテスト対象の振る舞いをテストすることもできます。

gMockで関数の呼び出しを確認する際にはEXPECT_CALLを記述するのですが、ポインタが絡むケースでハマりました。

以下が今回のケースの例です。

class AppleTree {
    // イニシャライザでメンバ変数に代入していない
    AppleTree(std::unique_ptr<Apple> apple) {} 
    int GetAppleLife() {
        return apple_->GetLife();
    }
    unique_ptr<Apple> apple_
}
    
TEST_F(AppleTreeTest, GetAppleLife) {
    Apple apple = std::make_unique<Apple>();
    AppleTree   = new AppleTree(std::move(apple));
    EXPECT_CALL(*apple, GetLife()).WillOnce(Return(100));
    int result = apple->GetAppleLife();
    EXPECT_EQ(result, 100);
}

コードを簡単に説明すると、
AppleTreeというクラスがあり、そのインスタンスメソッドであるGetLife()をテストしています。
AppleTreeはAppleに依存しており、GetAppleLifeでAppleのGetLifeを呼び出します。
そこで、AppleをモックしてGetLifeの呼び出しをチェックしようというケースですね。

AppleTreeはAppleをunique_ptrで受け取ります。そのためappleをstd::moveするのですが、実装コードのミスでメンバ変数に保存されません。

そのため、appleが破棄されてしまい、EXPECT_CALLの実行時に不正なポインタになってしまいます。
そのため、このテストを実行するとクラッシュしてしまいます。

実装コードの不具合はメンバ変数の初期化忘れのため、プログラムの実行時には未定義挙動になります。このようなケースもテストで分かるのはありがたいですね。
今回は以下のように修正することで解消されました。

class AppleTree {
    AppleTree(std::unique_ptr<Apple> apple) : apple_(apple) {}
...省略 

ただ、今回はクラッシュ時のダンプしか得られなかったので調べるのに時間がかかってしまいました。

デバッグしてもメソッドがUntypedFunctionMockerBaseになっているくらいしか分からず、辿っていくとmockObjをnullと比較している箇所があったので気づけた次第です。

参考になりましたら幸いです。

スポンサーリンク
ABOUT ME
サーモン
サーモン
著者(ソフトウェアエンジニア)
情報系大学院を卒業後、ソフトウェアエンジニアとして都内企業に従事。プログラミング歴は10年以上になります。
スポンサーリンク
記事URLをコピーしました