【问题标题】:Dependency Injection: Dependency needs field/member依赖注入:依赖需要字段/成员
【发布时间】:2020-01-19 10:56:20
【问题描述】:

我有两个类 A 和 B。目前,B 是在 A 内部构造的。由于我想在单元测试中验证 A 是否正确调用 B 的方法,所以我想将 B 注入 A而是启用模拟。

问题是:在构造时,B 需要 A 的字段之一作为构造函数参数。这意味着如果我注入 B 并因此将其构造移出 A,我还必须将该字段的构造移出 A。

这里最优雅的解决方案是什么?我希望我正确地解释了这个问题。如果没有,请随时提出任何问题。

尝试用代码 sn-ps 解释问题:

现在是什么样子:

// constructor of class A. b_ is being constructed. field_of_a_ is initialized.
A::A() : b_{field_of_a_}, field_of_a_{5} {

}

我想避免的:

// In the class constructing A...
int field_of_a;
B b(field_of_a);
A a(b, field_of_a);

// and inside A...
A::A(B& b, int& field_of_a) : b_{b}, field_of_a_{field_of_a} {

}

编辑 1:从我的一个 cmets 内部澄清:

是的,所以目前 B 拥有各种计时器和与计时器相关的方法。它的方法在消息处理期间由 A 调用。 B 需要访问位于 A 内部的队列和一些其他资源。在测试 A 时,我想验证在消息处理期间计时器是否正确启动/停止。

【问题讨论】:

  • 您对终身管理的偏好做得很好!也许也可以尝试在 Rust 中编译?
  • 我不明白是什么让你想到 Rust tbh :'D
  • 好吧,既然 Rust 对生命周期管理更加谨慎,那么在 Rust 中工作的任何东西都应该适合 C++。如果我没记错的话,你想要避免的一些解决方案确实会给 Rust 编译器带来麻烦,这应该证明你的观点。
  • 哦,我明白了,我不知道 :) 感谢您的澄清!

标签: c++ unit-testing testing dependency-injection


【解决方案1】:

您应该传递一个生成 B 的工厂,而不是传递 B。它仍然保持依赖注入。 工厂可以是虚拟类、函数指针、std::function、...

然后由 A 使用工厂。您的测试将简单地验证工厂在 A 的构建过程中使用。

更新:你的工厂应该是抽象的,并将一个实例返回到 A 需要的 B 的公共接口。在测试期间,可以对工厂进行编程以生成 B 的模拟实例。

此外,请确保在 A 的类定义中,首先声明传递给 B 的成员,然后声明持有 B 实例的成员。这是为了让第一个成员的寿命比 B 的寿命长。

【讨论】:

  • 感谢您的回答!我想评论您关于使用虚拟课程的观点。当然,在使用模拟对象时,我会先派生出 B 的接口——我们称之为 B_Interface。这仍然让我不得不在生产代码中构建 B_Interface 的实现 - 即 B -。同样的问题,因为 B 仍然需要 'field_of_a' 作为构造参数。
  • 工厂可以直接生成B,不需要B的接口。在你的案例中应该测试的是工厂:生产代码->它只是调用B的构造函数,测试代码->它首先告诉 gmock B 将被构造
  • 所以你说“它只是调用 B 的构造函数”。您的想法是将“field_of_a”变量传递给工厂的创建方法,那么如果我理解正确?因为工厂需要 'field_of_a' 来构建 B。
  • 没错。虽然我明白这段代码并没有显式测试 B 的构造函数,但它只是通知构造函数将被调用。确切的代码将取决于我猜想/需要验证的确切内容。
  • 绝对!我以这种方式编写并测试了很多代码。我看你真的明白了
猜你喜欢
  • 1970-01-01
  • 2013-04-30
  • 2019-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 2020-04-14
相关资源
最近更新 更多