Inyección de dependencia con unique_ptr para burlarse
Tengo una clase Foo que usa la clase Bar. Bar se usa solo en Foo y Foo está administrando Bar, por lo tanto, uso unique_ptr (no es una referencia, porque no necesito Bar fuera de Foo):
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_;
};
Hasta ahora todo bien, esto funciona bien. Sin embargo, tengo un problema cuando quiero probar el código de la unidad:
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();
}
La prueba falla porque el objeto simulado fue transferido para Foo, y el establecimiento de una expectativa sobre dicho simulacro falla.
Posibles opciones de DI:
by shared_ptr: es demasiado en este caso (el objeto Bar no es compartido entre Foo por ningún otro motivo)por referencia a IBar: no es una opción (la barra no se almacena fuera de Foo, por lo que el objeto de barra creado se destruiría dejando a Foo con una referencia colgante)por unique_ptr: no es comprobable en la forma presentadapasando por valor: no es posible (se producirá copia, el mismo problema que con unique_ptr).La única solución que obtuve es almacenar el puntero sin procesar a BarMock antes de que Foo se convierta en el único propietario de BarMock, es decir:
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;
};
¿No hay una solución más limpia? ¿Tengo que usar la inyección de dependencia estática (plantillas)?