匿名の名前空間はコードをテスト不能にします


13

典型的なC ++コードは次のとおりです。

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

それはまともな慣用的なC ++コードのように見えます-クラス、そのヘルパー関数のいくつかの定数matchによって使用されるFooヘルパー関数。

そして、テストを書きたいです。
それはmatch全く自明ではないので、別個の単体テストを記述することは完全に論理的です。
ただし、匿名の名前空間に存在します。
もちろん、私は呼び出すテストを書くことができますFoo::f()。しかし、Foo重くて複雑な場合、良いテストにはなりません。そのようなテストは、他の無関係な要因から被験者を隔離しません。

そのmatchため、匿名の名前空間から移動する必要があります。

質問:関数や定数をテストで使用できなくする場合、匿名の名前空間に関数や定数を入れる意味は何ですか?


3
@BЈовићはコードを読み直します-匿名の名前空間はfoo.cppヘッダーではなくにあります!OPは、ヘッダーにanon名前空間を入れてはならないことをよく理解しているようです。
アモン

名前のない名前空間でのC ++コードの単体テストでのいくつかのアイデア...しかし、「ポイント」はカプセル化が良いということです。結局、プライベートメンバー関数の場合と同じ問題です。非侵入的にテストすることはできませんが、ユニットテストのために隠れている情報を放棄したくない(例:stackoverflow.com/a/3676680/3235496)。
マンリオ

1
概念的には、プライベートメソッドをテストする(またはテストしない)場合とそれほど違いはありません。プログラマーで「ユニットテストプライベート」を検索すると、ケースに直接適用できる多くの回答が得られます。
ドックブラウン

@DocBrown匿名で関数をテストする方法を尋ねているのではありません。名前空間。「なぜanon。名前空間にコードを入れるのか?」(質問の最後のテキストを参照)。Ixrecは、タイトルを別の名前に変更したことを責めます。
アビックス

1
@Abyx:上記の他の回答では、プライベートメソッドをテストすることは本当に悪いアイデアであり、friendその目的のためにキーワードを乱用することは推奨されないという、多くの専門家の大きなコンセンサスがあります。メソッドの制限が、それ以上直接テストできない状況につながる場合、プライベートメソッドが役に立たないことを意味します。
ドックブラウン

回答:


7

プライベートな実装の詳細を単体テストする場合は、名前のない名前空間に対して、プライベートな(または保護された)クラスメンバーと同じような回避を行います。

侵入してパーティー。

乱用するクラスではfriend、名前のない名前空間では#include-mechanism を乱用します。これにより、コードを強制的に変更することさえありません。
テストコード(またはすべてを公開するための何かだけが良い)が同じTUにあるので、問題はありません。

注意事項:実装の詳細をテストする場合、それらが変更されるとテストが中断します。とにかく漏れる実装の詳細のみをテストするか、テストが異常に短命であることを受け入れるようにしてください。


6

この例の関数は非常に複雑に見えますが、単体テストの目的で、ヘッダーに移動する方が適切な場合があります。

関数や定数をテストで使用できなくする場合、匿名のネームスペースに関数や定数を配置する意味は何ですか?

それらを他の世界から隔離するため。そして、匿名の名前空間に入れることができるのは関数と定数だけではありません-それも型のためです。

ただし、ユニットテストが非常に複雑になる場合は、間違っています。そのような場合、関数はそこに属しません。次に、テストをより簡単にするために少しリファクタリングする時が来ました。

そのため、匿名名前空間では、非常に単純な関数、場合によってはその変換単位で使用される定数と型(typedefを含む)のみを使用する必要があります。


5

一致する個別の単体テストを作成することは完全に論理的です。なぜなら、それは非常に重要だからです。

あなたが示したコードmatchは、トリッキーなエッジケースのない非常に簡単な1ライナーですか、それとも簡単な例ですか?とにかく、私はそれが簡素化されていると仮定します...

質問:関数や定数をテストで使用できなくする場合、匿名の名前空間に関数や定数を入れる意味は何ですか?

Deduplicatorが侵入して#includeトリックを介してアクセスするための完全に良い方法をすでに示しているので、この質問が私をここに飛び込ませたかったのです。しかし、ここでの言い回しは、すべてのすべての内部実装の詳細をテストすることは、ある種の普遍的な最終目標であるように思えます。

ユニットテストでさえも、機能の小さな粒状の内部マイクロユニットをすべてテストすることは常に目標ではありません。開発者が使用する理由同じ質問を尋ねることによっても、答えに質問難しくすることができますC.あなたの静的ファイル・スコープ機能に適用されるpimpls必要があろうC ++で両方 friendship#include改善コンパイル時間のための実装の詳細を簡単にテスト容易性を取引、白いボックスに策略を、例えば

ある種の実用的な観点からは、ひどく聞こえるかもmatchしれませんが、トリップする原因となるいくつかのエッジケースでは正しく実装されない可能性があります。しかし、唯一の外部クラス、場合Fooへのアクセスを持っている、matchおそらくの正しさに出会い、それらのエッジケース、それはむしろ無関係だという方法でそれを使用することはできませんFooということはmatchない限り、遭遇することはありませんこれらのエッジケースがあるFoo時点で変化を、のテストFooは失敗し、すぐにわかります。

あらゆる内部実装の詳細(おそらくミッションクリティカルなソフトウェアなど)をテストすることに熱中している、より強迫観念のある人は、侵入してパーティーに参加したいと思うかもしれませんが、多くの人々は、考えられる最も脆弱なテスト。YMMV。しかし、私はこの質問の言葉遣いに取り組みたいと思っていました。これは、この種の超微細粒度の内部詳細レベルのテスト容易性が最終目標でなければならないように聞こえます。最も厳格なユニットテストの考え方でもここで少しリラックスできるかもしれませんすべてのクラスの内部をX線撮影することは避けてください。

それでは、なぜC ++の匿名名前空間で関数を定義するのか、Cの内部リンケージを持つ外部の世界から隠されたファイルスコープの静的関数として関数を定義するのですか?そして、それは主にそれです:外の世界からそれらを隠すこと。これには、コンパイル時間の短縮から複雑さの軽減(他の場所ではアクセスできないものは他の場所では問題を引き起こすことができません)など、多くの効果があります。おそらく、プライベート/内部実装の詳細のテスト可能性は、ビルド時間を短縮し、不要な複雑さを外の世界から隠すなど、やり直したときに頭に浮かぶ一番のことではありません。


これに10回賛成できたらいいなと思います。ミッションクリティカルな高保証ソフトウェアに関連する接線として、あなたは細部の正確さにもっと関心があります。このC ++の慣用的なスタイルは、それに適していません。匿名の名前空間やプロキシのようなパターンのような言語の機能は使用しません。[サポートされている]ユーザースペースヘッダーと[サポートされていない]開発者スペースヘッダーで境界を粗く制御します。
デビッド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.