2016-11-14 109 views
0

four year old question使用第三方库,我有点怀疑。PHP 7中的猴子修补程序

仅出于测试目的,我想重新定义其中一个类的静态方法。看看下面的例子:

class Driver { 
    public static function getVersion() : string 
    { 
     // Retrieves a version from a system executable 
     return some_system_call(); 
    } 
} 

class Module { 
    public function methodToTest() 
    { 
     if (Driver::getVersion() === '4.0.0') { 
      // allow for additional options/methods 
     } else { 
      // use a subset 
     } 
    } 
} 

我需要为Driver::getVersion返回不同的版本字符串。我通常会嘲笑这个班级,但是既然这不是注入,也不是一个实例,它不会起作用。

我可以更改源代码,添加方法和属性测试,以便被测试的类永远不需要调用Driver,但在我看来,重构代码只是为了让测试“工作”并不是解决方案。

我正在考虑创建另一个Driver类,并以某种方式加载它以代替原来的位置。

我该怎么做?

+0

继承驱动程序类,使您需要的getVersion()方法使Driver2类,并使用Driver2而不是驱动程序 – SergeyLebedev

+0

一方面,您可以使用'runkit'的扩展来操作/重新定义测试时的行为(如在phpunit中嘲笑)另一方面,如果可能的话,你应该防止静态方法。在你的例子中是没有显示的类的真正交互。它们是否立即加载,是否有命名空间等等。 – JOUM

+0

Offtop,但有[version_compare](http://php.net/version_compare)功能 –

回答

2

你可能想使用像水木清华:

class Module 
{ 
    private $version; 

    public function __construct($version){ 
     $this->version = $version; 
    } 

    public function methodToTest() 
    { 
     if ($this->version === '4.0.0') { 
      // allow for additional options/methods 
     } else { 
      // use a subset 
     } 
    } 
} 

或另一种办法是注射并不版本,但应该是一个供应商(如果你知道你将有版本控制复杂的逻辑的一些位 - 让你可以分割逻辑ModuleProvider和适当时)之间:

class Module 
{ 
    private $versionProvider; 

    public function __construct($provider){ 
     $this->versionProvdier = $provider; 
    } 

    public function methodToTest() 
    { 
     if ($this->versionProvider->getVersion() === '4.0.0') { 
      // it could be even $this->versionProvider->newFeaturesAreSupported() 
     } else { 
      // some other stuff 
     } 
    } 
} 

和又一可以实现一些代理类等

class Module 
{ 
    public function methodToTest() 
    { 
     $myMonostateProxy = new MyMonostateProxy(); 
     $version = $myMonostateProxy->getVersion(); 
     if ($version === '4.0.0') { 
      // allow for additional options/methods 
     } else { 
      // use a subset 
     } 
    } 
} 

所以你可以单独模拟你的monostate(可能是通过反映在privtates或通过其公共界面,不管怎么说,不要忘了tearDown它)。真正实施它只会称之为不可控的Driver::getVersion()

我认为前两个选项比较干净,但需要一些创建的努力(因为你需要一些注射来执行)。 第三个隐藏的依赖性,在测试中有点棘手,因此不太干净,需要更多的努力来维护,但隐藏所有选择内部的东西,使定期使用更容易。

+0

现在我正在使用反射来更改'Driver'类中的版本属性。但是,我接受这个答案的原因是,注射是一个更好的选择。使用'DriverAdapter'类可以使测试更容易,并将所有底层系统细节封装到一个类中。 – Twifty

0
class Driver { 
    private static $testVersion; 

    public static function setTestVersion(string $testVersion = null) 
    { 
     static::$testVersion = $testVersion; 
    } 

    public static function getVersion() : string 
    { 
     if (static::$testVersion !== null) { 
      return static::$testVersion; 
     } 
     // Retrieves a version from a system executable 
     return some_system_call(); 
    } 
} 
+0

这需要一个公开的,并开放滥用,方法添加到驱动程序。 'Driver'类应该不需要修改。 – Twifty

+0

http://carbon.nesbot.com/docs/#api-testing使用这种模式。你可以删除'setTestVersion'函数并使用一些反射魔法来设置'$ testVersion'。 –

0

您可以注册一个类加载器,它以某种方式知道测试并从不同的位置加载修改后的Driver类。