2009-08-07 84 views
10

我使用SimpleTest单元测试了一些PHP代码,并且遇到了麻烦。在我对数据库类的测试中,我希望能够设置对PHP mysql函数的期望。在我对mail函数的封装类的测试中,我想模拟PHP mail函数。这些只是一些例子。在单元测试中嘲弄PHP函数

重点是:我不(总是)要测试我的Mail类是否发送电子邮件,我想测试它如何调用mail函数。我希望能够控制这些函数返回的内容。我希望能够测试我的数据库类而不需要数据库,灯具和整个场景。

我已经有了一些测试Ruby代码的经验,而Test :: Unit和RSpec使得测试代码非常容易。我新来测试PHP,感觉就像我正在测试的东西比我需要的多得多,以便让我的测试通过。

在SimpleTest或PhpUnit或其他一些测试框架中是否有这样的方法?

回答

10

未采用自动方式。你可以做的是编写你的代码,使得外部依赖包裹在从外部传入的对象中。在您的生产环境中,您只需连接真正的适配器,但在测试期间,您可以将它连接到存根或嘲笑。

如果你真的坚持,你可以使用runkit extension这改变了php的编程模型,这样你就可以在运行时重新定义类和函数。这是一个外部的和非标准的扩展,所以请记住。事实上的标准是一种手动方法,正如我上面所描述的那样。

+0

我明白了。我有点期待(但不希望)这样的答案。我并不坚持,因为我想简化和加速测试,而不是使其更加复杂。感谢您的答复! – avdgaag 2009-08-08 07:42:39

+0

现在[Mockery](http://docs.mockery.io/en/latest/cookbook/mocking_hard_dependencies.html)似乎是[要走的路](http://stackoverflow.com/a/42158443/659788)。 – Franco 2017-05-08 20:00:56

+1

@Franco不适合内置。 – troelskn 2017-05-09 11:25:55

0

在PHP 5.3+环境中,您可以解决该需求由黑客命名空间使用runkit扩展。唯一的要求是函数调用不使用像\mysql_query()这样的完全限定名称空间,而且它们通常不会。然后,您可以在测试中定义相同的函数,方法是在名称空间中定义测试,PHP将调用您的函数而不是全局函数。我个人使用这种方法来存根time()函数调用。这是一个nice example with the mockery framework

+0

你可以使用[php-mock](https:// github。com/php-mock/php-mock)来创建这样的模拟。 – 2016-04-15 10:45:32

1

Here is an interesting article写道嘲笑全球php功能。作者提出了一个非常有创意的解决方案,通过覆盖SUT命名空间内的方法(被测服务)来“模拟”(关闭)全局php函数。

这里从博客文章的例子,其中time功能嘲笑代码:

<?php 

namespace My\Namespace; 

use PHPUnit_Framework_TestCase; 

/** 
* Override time() in current namespace for testing 
* 
* @return int 
*/ 
function time() 
{ 
    return SomeClassTest::$now ?: \time(); 
} 

class SomeClassTest extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @var int $now Timestamp that will be returned by time() 
    */ 
    public static $now; 

    /** 
    * @var SomeClass $someClass Test subject 
    */ 
    private $someClass; 

    /** 
    * Create test subject before test 
    */ 
    protected function setUp() 
    { 
     parent::setUp(); 
     $this->someClass = new SomeClass; 
    } 

    /** 
    * Reset custom time after test 
    */ 
    protected function tearDown() 
    { 
     self::$now = null; 
    } 

    /* 
    * Test cases 
    */ 
    public function testOneHourAgoFromNoon() 
    { 
     self::$now = strtotime('12:00'); 
     $this->assertEquals('11:00', $this->someClass->oneHourAgo()); 
    } 
    public function testOneHourAgoFromMidnight() 
    { 
     self::$now = strtotime('0:00'); 
     $this->assertEquals('23:00', $this->someClass->oneHourAgo()); 
    } 
} 

不知道这是做一个很好的方式,但它确实工作得很好,我认为这是值得一提的这里。可能是一些讨论的食物...