使用unique_ptr进行依赖注入以进行模拟

Qua*_*rra 8 c++ unit-testing dependency-injection gmock

我有一个使用类Bar的类Foo.Bar仅在Foo中使用,而Foo正在管理Bar,因此我使用unique_ptr(不是引用,因为我不需要Foo之外的Bar):

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_;
};
Run Code Online (Sandbox Code Playgroud)

到目前为止一切顺利,这很好.但是,当我想对代码进行单元测试时,我遇到了问题:

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();
}
Run Code Online (Sandbox Code Playgroud)

测试失败,因为模拟对象被转移到Foo,并且设置对此类模拟的期望失败.

DI的可能选项:

  • by shared_ptr:在这种情况下太多了(在Foo之间没有任何其他东西共享Bar对象)
  • 通过引用IBar:不是一个选项(Bar不存储在Foo之外,因此创建的Bar对象将被破坏,使Foo具有悬空引用)
  • by unique_ptr:以所呈现的方式无法测试
  • 通过传递值:是不可能的(复制将发生 - 与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;
};
Run Code Online (Sandbox Code Playgroud)

是不是有更清洁的解决方案?我是否必须使用静态依赖注入(模板)?

Qua*_*rra 1

毕竟,我最终在各处都使用了这种方法:

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;
};
Run Code Online (Sandbox Code Playgroud)

它可以很好地与gtest/gmock.