假设我有以下课程:
class Foo
{
public:
Foo()
{
Bar();
}
private:
Bar(bool aSendPacket = true)
{
if (aSendPacket)
{
// Send packet...
}
}
};
我正在编写一个测试工具,需要通过工厂模式创建一个Foo
对象(即我没有直接实例化它)。我无法更改任何工厂实例化代码,因为这是在我无法访问的框架中。
由于各种原因,我不希望Bar
方法在从测试工具运行数据包时发送数据包。
假设我无法直接调用Bar
(消除使用朋友类等潜在的解决方案),在运行我的测试工具时,用什么优雅的设计模式来防止数据包被发送?我绝对不想在特殊情况下污染我的生产代码。
答案 0 :(得分:2)
您希望Bar
在普通操作中发送数据包,但不在测试中。因此,您必须拥有一些代码,这些代码在测试期间调用Bar
时运行,即使它是一个空函数。问题是在哪里提出来。
我们无法在if(aSendPacket)
循环中看到代码,但如果它将其工作委托给其他类,那么我们可以在那里进行替换。也就是说,如果循环是
if(aSendPacket)
{
mPacketHandler.send();
}
这样工作由`packetHandler class:
完成// packetHandler.cc
void packetHandler::send()
{
// do some things
// send the packet
}
然后我们可以制作packetHandler
类的“静音”版本。 (有些人会把它称为存根或模拟类,但似乎对这些术语的定义存在一些争议。)
// version of packetHandler.cc to be used when testing e.g. Foo
void packetHandler::send()
{
// do some things
// don't actually send the packet
}
在测试Foo
时,请编译此版本的packetHandler
并将其链接。工厂不会知道其中的差异。
另一方面,如果发送数据包的代码在Foo
中拼写出来,无法阻止Foo
类以外的行为,那么你必须要有一个{test}版本的Foo.cc
(还有其他方法,但它们很笨拙和危险),最好的方法取决于你的代码库的细节。如果只有这样的“不可测试”特性,那么最好将Foo::bar(...)
单独放入源文件中,使用两个版本(并对每个其他特殊方法执行相同操作)。如果有很多,那么可能值得推导出一个特定于测试的工厂类,它将构造例如class testingFoo : public Foo
覆盖Bar
。毕竟,这就是抽象工厂设计模式的用途。
答案 1 :(得分:0)
我会将'bar'视为按照模板方法发送数据的算法
// Automation Strategies
class AutomationStrategy{
public:
void PreprocessSend(bool &configsend) const {return doPreprocessSend(configsend);}
void PostprocessSend() const {return doPostprocessSend();}
virtual ~AutomationStrategy(){}
private:
virtual void doPreprocessSend(bool &configsend) const = 0;
virtual void doPostprocessSend() const = 0;
};
// Default strategy is 'do nothing'
class Automation1 : public AutomationStrategy{
public:
~Automation1(){}
private:
void doPreprocessSend(bool &configsend) const {}
void doPostprocessSend() const {}
};
// This strategy is 'not to send packets' (OP's intent)
class Automation2 : public AutomationStrategy{
public:
~Automation2(){}
private:
void doPreprocessSend(bool &configsend) const {
configsend = false;
}
void doPostprocessSend() const {}
};
class Foo{
public:
Foo(){
Bar();
}
private:
// Uses Template Method
void Bar(bool aSendPacket = true, AutomationStrategy const &ref = Automation1())
{
ref.PreprocessSend(aSendPacket); // Customizable Step1 of the algorithm
if (aSendPacket) // Customizable Step2 of the algorithm
{
// Send packet...
}
ref.PostprocessSend(); // Customizable Step3 of the algorithm
}
};
int main(){}
如果你不能修改'bar'界面,那么配置'Foo'接受它的构造函数中的测试自动化策略并存储它(稍后在调用'bar'时使用)
答案 2 :(得分:0)
这可能是一个粗略的过度简化,但我的第一个倾向是添加某种测试条件对象(实际上是一个变量库),它将所有内容都默认为false,然后将钩子放在代码中,你想要偏离标准行为测试,切换[有效全局]测试条件对象变量。无论如何,你将需要做同等的逻辑,其他一切看起来要么更复杂,对于理解对象内部的逻辑流程更具破坏性,或者更可能破坏测试用例中的行为。如果您可以使用最少量的条件开关位置/变量,这可能是最简单的解决方案。
我的意见,无论如何。