2013-10-30 28 views
1

我目前正在进行的项目包含面向对象和程序化PHP代码的混合。所以,我有这样的事情:在PHPUnit中,我该如何模拟不属于类的一部分的函数?

function doStuff($value) 
{ 
    $x = $value + 1; 

    return $x; 
} 

class MyClass 
{ 
    private $field; 

    public function setMyValue($amount) 
    { 
     $this->field = doStuff($amount) + doStuff(2 * $amount); 
    } 
} 

有几个这样的相关性,但也有少数(你可以指望他们用一只手)。但是,我需要为这些类编写单元测试(使用PHPUnit),我不知道如何模拟来自程序方面的函数(在本例中为doStuff)。从我所看到的,PHPUnit中的模拟功能仅适用于类。

我只是做了没有任何模拟,但问题是这些功能中的一些做一些IO操作;我不认为这是一个非常好的主意,不要以某种方式嘲笑他们。

我该如何解决这个问题?

+0

看一看http://marcelog.github.io/articles/php_mock_global_functions_for_unit_tests_with_phpunit .html – busypeoples

+0

您是否从任何人获得令人满意的答复以接受,还是仍然存在与这些建议有关的问题? –

+0

@StevenScott其实我决定不再为这个类编写单元测试。太多的屁股疼痛。 –

回答

0

我看到的唯一选择是依赖注入,因为你的类想要使用类之外的资源。因此,这破坏了一些封装规则。

我过去如何做到这一点,是将这些函数放在自己的类中并且要求/包含它们,并且当设置了测试变量时,包含一个具有相同半“模拟”函数的基本文件返回已知状态。

我这样做的另一种方法是创建一个简单的UTILITY类,它包含所有这些数据函数,然后使用依赖注入和mock来进行测试。

class Utilities 
{ 

    function doStuff($value) 
    { 
     $x = $value + 1; 
     return $x; 
    } 
} 

class MyClass 
{ 
    private $UtilitiesObject; 

    private $field; 

    public function setMyValue($amount) 
    { 
//  $this->field = doStuff($amount) + doStuff(2 * $amount); 
     $this->field = $this->UtilitiesObject->doStuff($amount) + $this->UtilitiesObject->doStuff(2 * $amount); 
    } 
} 

    // Constructor Injection, pass the Utilities object here 
    public function __construct($Utilities = NULL) 
    { 
     if(! is_null($Utilities)) 
     { 
      if($Utilities instanceof Utilities) 
      { 
       $this->SetUtilities($Utilities); 
      } 
     } 
    } 

    function SetUtilities(Utilities $Utilities) 
    { 
     $this->UtilitiesObject = $Utilities 
    } 

} 

测试:

class UtilitiesTest extends PHPUnit_Framework_TestCase 
{ 

    // Could also use dataProvider to send different returnValues, and then check with Asserts. 
    public function testSetMyValue() 
    { 
     // Create a mock for the Utilities class, 
     // only mock the doStuff() method. 
     $MockUtilities = $this->getMock('Utilities', array('doStuff')); 

     // Set up the expectation for the doStuff() method 
     $MockUtilities->expects($this->any()) 
        ->method('doStuff') 
        ->will($this->returnValue(1)); 

     // Create Test Object - Pass our Mock as the Utilities 
     $TestClass = new MyClass($MockUtilities); 
     // Or 
     // $TestClass = new MyClass(); 
     // $TestClass->SetUtilitiess($MockUtilities); 

     // Test doStuff 
     $amount = 10; // Could be checked with the Mock functions 
     $this->assertEquals(2, $TestClass->doStuff($amount));  // Mock always returns 1, so 1+1=2 
    } 
} 
0

您可以尝试重新定义 “模拟” 这些功能。

一种选择使用PHP库 - Patchwork

Patchwork\replace("size", function($x) 
{ 
return "huge"; 
}); 

或者硬编码解决方案 - runkit扩展(通过PECL安装) -

Runkit

bool runkit_function_redefine (string $funcname , string $arglist , string $code) 
1

当你打电话给你的函数(在全局命名空间中定义),并且始终将它们称为不合格,您可以利用PHP's namespace fallback policy

如果名称空间函数不存在,PHP将回退到全局函数。

这允许你通过在调用者的命名空间中提供函数来创建一个模拟。

,使您的生活特别简单,我装成可一起使用PHPUnit使用的库php-mock-phpunit此:

namespace foo; 

use phpmock\phpunit\PHPMock; 

class BuiltinTest extends \PHPUnit_Framework_TestCase 
{ 

    use PHPMock; 

    public function testTime() 
    { 
     $time = $this->getFunctionMock(__NAMESPACE__, "time"); 
     $time->expects($this->once())->willReturn(3); 

     $this->assertEquals(3, time()); 
    } 
} 
相关问题