如何模拟在函数中创建的类(新运算符)?
Posted
技术标签:
【中文标题】如何模拟在函数中创建的类(新运算符)?【英文标题】:How to mock a class that is created in a function (new operator)? 【发布时间】:2021-09-18 15:49:18 【问题描述】:我正在尝试模拟一个在函数中创建的类。
function pon_validate_device(DB $db, Logger $logger, $request, $mode = 'add')
...
$testConnection = new Telnet(...);
$testConnection->login();
return true;
有没有办法模拟telnet类,控制登录功能的响应?
【问题讨论】:
【参考方案1】:有没有办法模拟telnet类,控制登录功能的响应?
不容易。创建 telnet 类的模拟 很 很容易,但是 pon_validate_device()
函数会创建具体的 Telent 对象(new
Telnet()
),没有简单的方法可以在调用其登录方法的地方注入要使用的模拟:
$testConnection = new Telnet(...);
$testConnection->login();
所以你应该认为这是不可能的,除非函数 pon_validate_device()
允许注入,例如使用(附加)函数参数:
function pon_validate_device(
DB $db,
Logger $logger,
$request, $mode = 'add',
Telnet $testConnection = null, # new parameter
)
...
$testConnection ?= new Telnet(...);
$testConnection->login();
return true;
然后您可以注入(在您的测试中)模拟。
通过将其设为非可选参数,可以使这种依赖关系更加明显。
由于该函数已经有四个(三加一可选)参数,看起来已经有点拥挤了。
参数注入的替代方法是构造函数注入。这可能需要您将函数转换为对象并实例化它:
class PonDevice
public function __construct(
private DB $db,
private Logger $logger,
private Telnet $telnet,
)
...
public function validate($request, $mode = 'add')
...
$this->telnet->login(???);
return true;
正如您在这个快速示例中已经看到的那样,它可能会冒泡初始化要求,例如参数化了什么
$testConnection = new Telnet(...);
现在已经被注入了,所以 Telnet 类可能需要额外的改变(如果 Telnet 构造函数做了实际工作,这可能很好,因为它使这些类很难在测试中使用 - 从构造函数注入带参数的方法,用问号快速勾勒:
$this->telnet->login(???);
已经说了很多,主要的收获应该是,如果测试表明某些代码很难测试,它可能会揭示以前更多隐藏的依赖关系。考虑一下正在发生的事情,并考虑代码是否可以从设计更改中受益(只有当它使被测系统更容易为您使用/开发时才有好处,例如测试工作轻而易举,您可以然后更快更安全地更改更多代码)。
现在是脏的部分(湿的时候很滑,而且总是湿的情况):
鉴于被测代码尚未执行,更重要的是,从未实例化过 Telnet 类并且正在使用自动加载,您可以“模拟” Telnet 类定义,将其替换为您自己的(具有相同的类名)。
当代码随后执行时,它将根据该类定义实例化 Telnet 对象,因为不再有任何东西可以自动加载。
但是,这需要您完全复制 Telnet 类,然后为该测试添加双倍类特定测试点以注入仅测试行为和属性。
正如您可以想象的那样,这很麻烦、不稳定、维护起来很混乱,而且无法进一步改进被测代码。
但是我认为你要求它,至少应该在答案中提到它。
【讨论】:
以上是关于如何模拟在函数中创建的类(新运算符)?的主要内容,如果未能解决你的问题,请参考以下文章