regular types と value semantics のファンとして、クラスがより規則的になり、非多態性になることを望んでいます。スローしない操作のファンとして、私はnoexcept
である操作に熱心です。また、コンパイラーによって実際に生成されている特殊なメンバー関数の確認にも感謝します。そして、これらが関連付けられているコードと一緒にこれらのものを追跡したいと思います。
C++は <type_traits>
ヘッダーファイルを提供します。このヘッダーファイルは、このような品質をチェックするためのクラスを提供し、次のような単体テストコードでそれらを利用するのを楽しんでいることがわかりました。
TEST(MyClass, Traits)
{
EXPECT_TRUE(std::is_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_constructible< MyClass >::value);
EXPECT_TRUE(std::is_copy_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_copy_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_copy_constructible< MyClass >::value);
EXPECT_TRUE(std::is_move_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_move_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_move_constructible< MyClass >::value);
EXPECT_TRUE(std::is_copy_assignable< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable< MyClass >::value);
EXPECT_FALSE(std::is_trivially_copy_assignable< MyClass >::value);
EXPECT_TRUE(std::is_move_assignable< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_move_assignable< MyClass >::value);
EXPECT_FALSE(std::is_trivially_move_assignable< MyClass >::value);
EXPECT_TRUE(std::is_destructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_destructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_destructible< MyClass >::value);
EXPECT_TRUE(!std::is_polymorphic< MyClass >::value);
}
私のソースコードリビジョンシステムにも入る単体テストコードとして、これにはMyClass
にコミットされるコードとともにこれらの品質を追跡できるという利点があります。 gamification クラスがこれらの品質をより多く発揮するのを見るのも好きです-これはこのようなコードを使用して行います。
しかし、私はこれを達成するために、多くの落とし穴なしでより多くの利点をもたらす他の方法があるのだろうか?
このテクニックに関して私が抱えているいくつかの懸念事項を次に示します。
static_assert
を使用して、クラスのコード自体などの他の場所でこれらの品質を追跡する方が良いでしょうか?これは、(他のソフトウェア開発者がこれを単体テストとして受け入れるかどうかではなく)それらがより良い場所であるかどうかに現在焦点を当てている最後の懸念とは異なります。示されているような単体テストコードではなくstatic_assert
を介してクラスのコードでこれらの品質を追跡することは、これらの品質の重要性の高さのように見えます。よりパフォーマンスが重要なクラスでは、その方向性は理にかなっています。私はまた、これらの重要度を(テストの失敗やビルドの失敗の代わりに)単なる警告またはメッセージにすることよりも重要度を下げる必要があるという見方も認識しています。EXPECT_*
コンテキストに解析するのは面倒です。その間、マクロ関数は単体テストのコンテキストで評価されますが、マルチステートメントマクロ関数は、1つのソースコード行(マクロの呼び出し元の行)としてすべて表示されます。また、これらのチェックの通常、nothrow
、およびtrivially
形式を組み合わせて、1つの行のチェックに入れ、パラメーターを使用して、これらのいずれをポジティブに適用するかを示します。しかし、このパターンほど満足できるものはまだありません。いいえ。これらは受け入れ可能な単体テストではありません。単体テストではimplementationではなくbehaviourをチェックする必要があります。あなたが持っているユニットテストはMyClass
の動作をチェックしていません。
私が別の開発者であり、ソフトウェアスイートの通常の型を本当に気にしていないとします。何らかの理由で、コピーコンストラクターを削除したいとします。コードはコンパイルされますが、突然この単体テストが失敗します。私は今何をしますか?おそらく単体テストを削除して続行してください。そして、もし私が新しいクラスを書いたら、これらのテストを追加することを一瞬も考えません。
static_assert
を使用して、クラスのコード自体などの他の場所でこれらの品質を追跡する方が良いでしょうか?確かに、static_assert
は単体テストよりも優れたソリューションです(それが実装できる場合でも)。しかし、それはまだ役に立たないと思います。部分的には単体テストと同じ理由で。チームの別の開発者がそれらを追加しますか?おそらく、毎回すべてのアサーションをコピーするか、それらを生成する醜いマクロを作成することになります。
最も深刻な問題を特定しました。また、コード標準の何かを変更したい場合、すべてのクラスだけでなく、すべてのテストを更新するか、コピーしたことをアサートする必要があります。待って、私はコード標準に言及しましたか?はい、これはあなたのコード標準の一部であるべきだと思います。チームは通常、コーディング方法に関するコード標準を定義します。これはコードスタイル(明らかに同意する必要がある)だけでなく、ゼロの規則に従う、保護されたメンバー変数を使用しない、クラスを通常の型として動作させるなどのことも行います。次に、他の開発者がこれらのコード標準への準拠をチェックするレビューシステムがあります。はい、人はすり抜ける可能性がありますが、テストとアサートを行わずにレビューしないと、保証もありません。
これらの特性が型のインターフェースの一部である場合、理想的には、その型の宣言の近くで表明する必要があります。 compilationは(テストだけでなく)失敗するため、static_assert
はそのための理想的な方法です。
一般に、テストは、例によってプログラムの特定のプロパティをチェックするのに役立ちます。彼らは通常、プログラムにバグがないことを示すことはできませんが、バグが存在する場合はバグを見つけるのに役立ちます。対照的に、型システムは正確性のプロパティの自動証明を提供します。型システムは(単なるシナリオの例ではなく)証明であるため、可能な場合は通常、型システムレベルのチェックを使用することをお勧めします。ここで、タイプ特性はタイプシステムレベルのプロパティであり、ランタイムプロパティではありません。単体テストスイートの一部としてそれらをチェックすることは間違っていませんが、混乱を招きます。