【问题标题】: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类,控制登录功能的响应?

【问题讨论】:

    标签: php mocking phpunit


    【解决方案1】:

    有没有办法模拟telnet类,控制登录功能的响应?

    不容易。创建 telnet 类的模拟 很容易,但是 pon_validate_device() 函数会创建具体的 Telent 对象(newTelnet() ),没有简单的方法可以在调用其登录方法的地方注入要使用的模拟:

        $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 类,然后为该测试添加双倍类特定测试点以注入仅测试行为和属性。

    正如您可以想象的那样,这很麻烦、不稳定、维护起来很混乱,而且无法进一步改进被测代码。

    但是我认为你要求它,至少应该在答案中提到它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多