gpt4 book ai didi

c++ - 使用 unique_ptr 进行依赖注入(inject)以模拟

转载 作者:可可西里 更新时间:2023-11-01 17:57:26 25 4
gpt4 key购买 nike

我有一个使用 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();
}

测试失败,因为 mock 对象被转移到 Foo,并且对这样的 mock 设置期望失败。

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

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

最佳答案

实际上我不会在生产环境中推荐这种东西,但是 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 );

The aliasing constructor: constructs a shared_ptr which shares ownership information with r, but holds an unrelated and unmanaged pointer ptr. Even if this shared_ptr is the last of the group to go out of scope, it will call the destructor for the object originally managed by r. However, calling get() on this will always return a copy of ptr. It is the responsibility of the programmer to make sure that this ptr remains valid as long as this shared_ptr exists, such as in the typical use cases where ptr is a member of the object managed by r or is an alias (e.g., downcast) of r.get()

关于c++ - 使用 unique_ptr 进行依赖注入(inject)以模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40508033/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com