Beyond the State-of-the-Art

最先端を超えたいと思ってる(大嘘)エンジニアのブログ

C++部・ユニットテストの裏技 〜private関数をテストする〜

Qiitaからの移植です。

はじめに

C++ Advent Calendar 2019の7日目の記事です。21日目に参加登録していますが、枠が空いてたので追加で登録しました。しょうもない小ネタを書きます。

private関数のユニットテストしたくない?

何らかのフレームワーク、例えばGoogle Test等を使って、ユニットテストをする状況を考えます。

クラスの振る舞いをテストコードで担保したいが、private関数に複雑なロジックが入っていて、どうしてもprivate関数の振る舞いをテストコードで担保したい場合があると思います。しかし、private関数にはクラスの外からアクセスできません。さあ、どうしましょう?

幸運なことに、C++には便利な機能があります。defineマクロです。これを使えば、なんと、ソースコードのコンパイル前に文字列を置換することができます!

defineマクロを使って、private関数をクラスの外からアクセスできるようにしましょう。テストコードで次のように書きます。実際に現場のテストコードでも見たことがあります。

#define private public

このマクロでソースコード中のprivateはすべてpublicに置き換わり、元々private関数だったものがpublic関数となり、クラスの外からアクセスできるようになります。めでたし、めでたし(?)

ただし非合法

C++17の規格書には次のように書かれています。

A translation unit shall not #define or #undef names lexically identical to keywords, to the identifiers listed in Table 4, or to the attribute-tokens described in [dcl.attr].

キーワードと同じ名前を#defineすることは、規格として禁止されています。つまり#define private public非合法な手段です。プロダクションコードに手を加えずにprivate関数をユニットテストするための手段としてはお手軽ですが、C++の世界では非合法であることをお忘れなく。なお、privateキーワードを省略して定義した場合はこの手段は使えません。

なお、合法的にprivate関数のテストをする方法については、 [C++] Private な関数のテスト を参考にしてください。

そもそもprivate関数のユニットテストをしなかればいけない状況とは?

たぶんクラス設計をミスってると思います。private関数でユニットテストしないといけない状況は、その関数に複雑なロジックが入ってるからがほとんどだと思います。

それではなぜ複雑なロジックが入ってしまってるかというと、private関数に仕事を押し付けすぎか、クラスの責務が大きすぎだと思います。前者の場合はprivate関数内の一部の処理をクラスの外に出して関数としてまとめる、後者の場合は責務が小さくなるようにクラスを分割する、という風にリファクタリングすることになると思います。

いずれにせよprivate関数のユニットテストの必要性が出てきた場合は、設計を見直さないといけない機会だと思います。

まとめ

  • #define private publicでprivate関数もユニットテストできるようになる(ただし非合法)
  • そもそもprivate関数のユニットテストをしないといけない状況は、設計を見直さないといけない機会かも