2016-11-09 2 views
6

私はクラスBarを使用するクラスFooを持っています。 (私はFooの外でバーを必要としないので、ないの参照を)ので、私はunique_ptrを使用して、バーだけはFooで使用され、fooが管理バーです:モックするためのunique_ptrの依存性注入

using namespace std; 
struct IBar { 
    virtual ~IBar() = default; 
    virtual void DoSth() = 0; 
}; 

struct Bar : public IBar { 
    void DoSth() override { cout <<"Bar is doing sth" << endl;};  
}; 

struct Foo { 
    Foo(unique_ptr<IBar> bar) : bar_(std::move(bar)) {} 

    void DoIt() { 
    bar_->DoSth(); 
    } 
private: 
    unique_ptr<IBar> bar_; 
}; 

は、これまでのところは良い、これは正常に動作します。私は、コードのユニットテストしたい場合しかし、私は問題を抱えている:

namespace { 
struct BarMock : public IBar { 
    MOCK_METHOD0(DoSth, void()); 
}; 
} 

struct FooTest : public Test { 
    FooTest() : barMock{ make_unique<BarMock>() }, out(std::move(barMock)) {} 

    unique_ptr<BarMock> barMock; 
    Foo out; 
}; 

TEST_F(FooTest, shouldDoItWhenDoSth) { 
    EXPECT_CALL(*barMock, DoSth()); 

    out.DoIt(); 
} 

モックオブジェクトはFooのfoは転送され、失敗したなモックの期待を設定したため、テストが失敗しました。 DIの

可能なオプション:shared_ptrのことで

  • :IBARを参照して
  • (バーオブジェクトはFooの間、他のすべてのものを共有していない)、この場合、多すぎる:オプションではありません( BarはFooの外部には格納されないので、作成されたBarオブジェクトは破壊され、Fooは参照が残っています)
  • by unique_ptr:提示された方法ではテストできません
  • 値渡しでは不可能ですoccure - unique_ptrと同じ問題)。

私が得た唯一の解決策は、fooがBarMockのもっぱら所有者になる前にBarMockに生のポインタを格納することで、すなわち:

struct FooTest : public Test { 
    FooTest() : barMock{new BarMock} { 
    auto ptr = unique_ptr<BarMock>(barMock); 
    out.reset(new Foo(std::move(ptr))); 
    } 

    BarMock* barMock; 
    unique_ptr<Foo> out; 
}; 

はきれいな解決策はありませんか?静的依存関係注入(テンプレート)を使用する必要がありますか?

+2

の)、ダウンキャストあなたは、[この回答](http://stackoverflow.com/questions/7616475/can-google-mock-a-method-with-a-smart-pointer読書に興味がある可能性があり-return-type/11548191#11548191)。 –

+0

@πάνταῥεῖ:リンクありがとうございます。私はすでにそれを見てきましたし、unique_ptrをパラメータとして取るメソッドでも動作しますが、コンストラクタにこのアプローチを適用できるかどうかはわかりません。 – Quarra

答えて

2

私は実稼働環境では実際にはお勧めしませんが、エイリアシングコンストラクタshared_ptrは多分あなたの場合には汚れていて、実用的な解決策を表しています。
最小限、実施例(つまりGTESTを使用していません、申し訳ありませんが、私はモバイルアプリからだと直接それをテストすることはできません):

#include<memory> 
#include<iostream> 
#include<utility> 

struct IBar { 
    virtual ~IBar() = default; 
    virtual void DoSth() = 0; 
}; 

struct Bar : public IBar { 
    void DoSth() override { std::cout <<"Bar is doing sth" << std::endl;};  
}; 

struct Foo { 
    Foo(std::unique_ptr<IBar> bar) : bar(std::move(bar)) {} 

    void DoIt() { 
     bar->DoSth(); 
    } 
private: 
    std::unique_ptr<IBar> bar; 
}; 

int main() { 
    std::unique_ptr<Bar> bar = std::make_unique<Bar>(); 
    std::shared_ptr<Bar> shared{std::shared_ptr<Bar>{}, bar.get()}; 
    Foo foo{std::move(bar)}; 
    shared->DoSth(); 
    foo.DoIt(); 
} 

私はあなたのテストはこのようなものになるだろうと思います:

struct BarMock: public IBar { 
    MOCK_METHOD0(DoSth, void()); 
}; 

struct FooTest : public testing::Test { 
    FooTest() { 
     std::unique_ptr<BarMock> bar = std::make_unique<BarMock>(); 
     barMock = std::shared_ptr<BarMock>{std::shared_ptr<BarMock>{}, bar.get()}; 
     out = std::make_unique<Foo>{std::move(bar)}; 
    } 

    std::shared_ptr<BarMock> barMock; 
    std::unique_ptr<Foo> out; 
}; 

TEST_F(FooTest, shouldDoItWhenDoSth) { 
    EXPECT_CALL(*barMock, DoSth()); 
    out->DoIt(); 
} 

aliasing constructorは何をしますか?

template< class Y >  
shared_ptr( const shared_ptr<Y>& r, element_type *ptr ); 

エイリアシングコンストラクタは:rと所有情報を共有するが、無関係とアンマネージポインタptrを保持shared_ptrを構築します。このshared_ptrが範囲外に出る最後のグループであっても、もともとrで管理されているオブジェクトのデストラクタを呼び出します。ただし、get()を呼び出すと、常にptrのコピーが返されます。例えば(別名このptrは、このようなptrrによって管理されるオブジェクトのメンバであるか、または一般的な使用例のように、このshared_ptrが存在する限りとして有効なままであることを確認するために、プログラマの責任でありますr.get()

+1

+1 shared_ptrのエイリアシングコンストラクタです。私が正しく理解していれば、aliasingコンストラクタで作成されたshared_ptrは渡されたptrを管理していないので、通常の生ポインタとして機能します。または生のptrの代わりにそれを使用する利点はありますか? – Quarra