依赖注入,unique_ptr模拟
Dependency injection with unique_ptr to mock
我有一个使用类 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_;
};
到目前为止一切顺利,这工作正常。但是,当我想对代码进行单元测试时,我遇到了问题:
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 中,并且对此类模拟对象设置期望失败。
DI 的可能选项:
- by shared_ptr: 在这种情况下太多了(Bar 对象在 Foo 之间不共享任何其他内容(
- 通过引用 IBar: 不是一个选项(Bar 不存储在 Foo 之外,因此创建的 Bar 对象将被破坏,留下 Foo 悬空引用(
- 按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;
};
难道没有更清洁的解决方案吗?我必须使用静态依赖注入(模板(吗?
实际上不是我建议在生产环境中使用的东西,但是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();
}
别名构造函数有什么作用?
template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type *ptr );
别名构造函数:构造一个与
r
共享所有权信息的shared_ptr
,但持有不相关且不受管理的指针ptr
。即使此shared_ptr
是组中最后一个超出范围的,它也会调用最初由r
管理的对象的析构函数。但是,对此调用get()
将始终返回ptr
的副本。程序员有责任确保只要此shared_ptr
存在,此ptr
就保持有效,例如在典型的用例中,ptr
是r
管理的对象的成员,或者是r.get()
的别名(例如,向下转换(
在将模拟对象传递给构造函数之前,可以保留对模拟对象的引用。 我认为由于成员初始化排序,它使代码有点脆弱,但在语义上它的含义更清楚。 BarMock
的所有权仍然只属于Foo
,引用句柄由FooTest
保留(类似于这个答案(。
与您的答案基本相同,但使用引用而不是原始指针
class FooTest : public ::testing::Test
{
protected:
FooTest() :
bar_mock_ptr(std::make_unique<BarMock>()),
bar_mock(*bar_mock_ptr),
foo(std::move(bar_mock_ptr))
{}
private:
// This must be declared before bar_mock due to how member initialization is ordered
std::unique_ptr<BarMock> bar_mock_ptr; // moved and should not be used anymore
protected:
BarMock& bar_mock;
Foo foo; //ensure foo has the same lifetime as bar_mock
}
毕竟,我最终在任何地方都使用了这种方法:
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;
};
它适用于gtest/gmock
.
- 如何使用Google Mock来模拟gettimeofday()
- G锁定铸造到基础上会释放模拟行为
- 有什么好的方法可以让系统调用代理允许在单元测试中进行模拟
- 落砂模拟碰撞检测C++和SFML
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在gtest.中使用fff.h模拟系统API
- 谷歌模拟和覆盖关键字
- 用C#中的并集模拟C++嵌套结构
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 我写了一个C++程序来模拟Enigma机器.我没有得到输出
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 如何模拟不同边数的骰子滚动?
- 模拟持久按键
- 使用SIR模型的疾病爆发模拟
- 在 c++ 中模拟输入并在 JAVA 中读取它?
- 为共享 ptr 向量实现复制 c'tor?
- 转发变量参数列表以模拟 std::thread
- 如何在谷歌模拟中匹配 C 样式数组