クラスがあるとします
class Inner {
public:
void doSomething();
};
class Outer {
public:
Outer(Inner *inner); // Dependency injection.
void callInner();
};
適切な単体テストでは、 のテストが必要ですInner
。次に、完全なスタック/の代わりに追加された機能に対して単体テストを実行できるようにOuter
、実数ではなく を使用するInner
テストを行う必要があります。MockInner
Outer
Outer
Inner
そうするために、GoogletestInner
は次のような純粋な抽象クラス (インターフェース) に変えることを提案しているようです:
// Introduced merely for the sake of unit-testing.
struct InnerInterface {
void doSomething() = 0;
};
// Used in production.
class Inner : public InnerInterface {
public:
/* override */ void doSomething();
};
// Used in unit-tests.
class MockInner : public InnerInterface {
public:
/* override */ void doSomething();
};
class Outer {
public:
Outer(Inner *inner); // Dependency injection.
void callInner();
};
したがって、製品コードでは、Outer(new Inner)
;を使用します。テスト中に、Outer(new MockInner)
.
わかった。理論的にはいいように思えますが、コード全体でこのアイデアを使い始めたとき、私はすべての異常なクラスに対して純粋な抽象クラスを作成していることに気付きました。不要な仮想ディスパッチのために低下するわずかなランタイム パフォーマンスを無視できるとしても、定型的なタイピングがたくさんあります。
別のアプローチは、次のようにテンプレートを使用することです。
class Inner {
public:
void doSomething();
};
class MockInner {
public:
void doSomething();
};
template<class I>
class Outer {
public:
Outer(I *inner);
void callInner();
};
// In production, use
Outer<Inner> obj;
// In test, use
Outer<MockInner> test_obj;
これにより、ボイラーメッキと不要な仮想ディスパッチが回避されます。しかし、今ではコードベース全体がおかしなヘッダー ファイルに含まれているため、ソースの実装を隠すことができません (イライラするテンプレート コンパイル エラーと長いビルド時間への対処は言うまでもありません)。
これらの 2 つの方法、バーチャルとテンプレートは、適切な単体テストを行う唯一の方法ですか? 適切な単体テストを行うためのより良い方法はありますか?
適切な単体テストとは、各単体テストがそのユニットによって導入された機能のみをテストし、ユニットの依存関係もテストしないことを意味します。