2016-11-09 15 views
6

Mam klasę Foo, która używa paska klasy. Bar jest używany tylko w Foo Foo i zarządza bar, dlatego używam unique_ptr (nie odwołanie, ponieważ nie muszę Bar poza Foo):Wstrzyknięcie zależne z unique_ptr na próbę

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_; 
}; 

tej pory tak dobrze, to działa prawidłowo. Jednak mam problem, gdy chcę do badanej jednostki kod:

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(); 
} 

test zakończy się niepowodzeniem, ponieważ obiekt został przeniesiony mock fo Foo i ustawienie oczekiwanie na takie makiety zawiedzie.

Możliwe opcje DI:

  • przez shared_ptr: jest zbyt wiele w tym przypadku (object Bar nie jest dzielona pomiędzy Foo żadnego cokolwiek innego)
  • przez odniesienie do IBAR: nie jest rozwiązaniem (Pasek nie jest przechowywany poza Foo, więc utworzony obiekt Baru zostanie zniszczony, pozostawiając Foo z wiszącym odwołaniem)
  • przez unique_ptr: nie można przetestować w przedstawionym sposobie
  • przez przekazanie wartości: nie jest możliwe (kopiowanie będzie occure - ten sam problem, co w przypadku unique_ptr).

Jedynym rozwiązaniem mam jest przechowywanie surowego wskaźnik BarMock przed Foo stać wyłącznie właściciel BarMock, tj .:

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; 
}; 

Czy nie czystsze rozwiązanie? Czy muszę używać iniekcji statycznej (szablony)?

+2

Możesz być zainteresowany w czytaniu [ta odpowiedź] (http://stackoverflow.com/questions/7616475/can-google-mock-a-method-with-a-smart-pointer -return-type/11548191 # 11548191). –

+0

@ πάντα ῥεῖ: dziękuję za link. Już to widziałem i działa dla metod, które biorą unique_ptr jako parametr - ale nie jestem pewien, czy można zastosować to podejście dla konstruktorów. – Quarra

Odpowiedz

2

Nie jest to coś, co polecałbym w środowisku produkcyjnym, ale aliasing konstruktor z shared_ptr reprezentuje może być brudne i działające rozwiązanie dla twojej sprawy.
Minimalny, przykład roboczych (czyli nie używa numeru GTEST, przepraszam, jestem z aplikacji mobilnej i nie można go przetestować bezpośrednio):

#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(); 
} 

Chyba twoja próba stanie się coś takiego :

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(); 
} 

Czego aliasing constructor zrobić?

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

aliasing konstruktora: konstruuje shared_ptr który dzieli informacje o własności z r, ale posiada niepowiązanych i niezarządzanej wskaźnik ptr. Nawet jeśli ta shared_ptr jest ostatnią z grupy, która wykracza poza zakres, wywoła destruktor dla obiektu pierwotnie zarządzanego przez r. Jednak wywołanie na tym get() zawsze zwróci kopię ptr. Obowiązkiem programisty jest upewnienie się, że ten numer ptr zachowuje ważność tak długo, jak długo istnieje shared_ptr, na przykład w typowych przypadkach użycia, gdzie ptr jest członkiem obiektu zarządzanego przez r lub jest aliasem (np., Przygnębiony) od r.get()

+1

+1 dla konstruktora aliasów shared_ptr, dobrze wiedzieć. Jeśli rozumiem to poprawnie, shared_ptr utworzony przy pomocy konstruktora aliasing nie zarządza przekazanym ptr, więc działa jako zwykły surowy wskaźnik. Czy jest jakaś przewaga w korzystaniu z niego zamiast surowego ptr? – Quarra