2009-08-07 86 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()); 
    } 
} 

不知道這是做一個很好的方式,但它確實工作得很好,我認爲這是值得一提的這裏。可能是一些討論的食物...