2009-08-10 79 views
4

我一直在使用Zend Framework進行單元測試。我已經習慣了它提供的其他東西,但我很難理解Mock Objects。Zend Framework:如何使用Zend_Service_Twitter單元測試模型

對於這個例子,我試圖使用模擬對象來測試我的模型。

<?php 

class Twitter_Model_Twitter 
{ 
    private $_twitter; 

    /** 
    * Make the options injectable. 
    * __contruct($auth, $key) 
    */ 
    public function __construct() 
    { 
     $config  = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV); 
     $key   = $config->encryption->salt; 
     $iv_size  = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB); 
     $iv   = mcrypt_create_iv($iv_size, MCRYPT_RAND); 
     $password = mcrypt_decrypt(MCRYPT_XTEA, $key, $password, MCRYPT_MODE_ECB, $iv); 

     $this->_twitter = new Zend_Service_Twitter($username, $password); 
    } 


    public function verifyCredentials() 
    { 
     return $this->_twitter->account->verifyCredentials(); 
    } 

    public function friendsTimeline($params) 
    { 
     return $this->_twitter->status->friendsTimeline($params); 
    } 
} 

對於我的單元測試:

require_once ('../application/models/Twitter.php'); 
class Model_TwitterTest extends ControllerTestCase 
{ 
    /** 
    * @var Model_Twitter 
    */ 
    protected $_twitter; 

    public function testfriendsTimeline() 
    { 
     $mockPosts = array('foo', 'bar');  

     //my understanding below is: 
     //get a mock of Zend_Service_Twitter with the friendsTimeline method 
     $twitterMock = $this->getMock('Zend_Service_Twitter', array('friendsTimeline')); 
     /* 
     line above will spit out an error: 
     1) testfriendsTimeline(Model_TwitterTest) 
     Missing argument 1 for Mock_Zend_Service_Twitter_9fe2aeaa::__construct(), called in 
     /Applications/MAMP/bin/php5/lib/php/PHPUnit/Framework/TestCase.php on line 672 and 
     defined /htdocs/twitter/tests/application/models/TwitterTest.php:38 
     */ 

     $twitterMock->expects($this->once()) 
       ->method('friendsTimeline') 
       ->will($this->returnValue($mockPosts)); 

     $model = new Twitter_Model_Twitter(); 
     $model->setOption('twitter', $twitterMock); 

     $posts = $model->friendsTimeline(array('count'=>20)); 
     $this->assertEquals($posts, $mockPosts); 
    } 
} 

你會如何測試以下? 1)的verifyCredentials() 2)的friendsTimeline()

感謝, Wenbert

回答

1

我要回答這個問題。我想我已經完成了這項工作,感謝#zftalk的zomg。

這是我的新Twitter的型號:

<?php 
//application/models/Twitter.php 
class Twitter_Model_Twitter 
{ 
    private $_twitter; 
    private $_username; 
    private $_password; 

    public function __construct(array $options = null) 
    {   
     if (is_array($options)) { 
      $this->setOptions($options); 
      $this->_twitter = new Zend_Service_Twitter($this->_username, $this->_password); 
     } else { 
      $twitterAuth = new Zend_Session_Namespace('Twitter_Auth'); 
      $config  = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV); 
      $key   = $config->encryption->salt; 
      $iv_size  = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB); 
      $iv   = mcrypt_create_iv($iv_size, MCRYPT_RAND); 
      $password = mcrypt_decrypt(MCRYPT_XTEA, $key, $twitterAuth->password, MCRYPT_MODE_ECB, $iv); 

      $username = $twitterAuth->username; 
      $this->_twitter = new Zend_Service_Twitter($username, $password); 
     } 
    } 

    public function setOptions(array $options) 
    { 
     $methods = get_class_methods($this); 
     foreach ($options as $key => $value) { 
      $pieces = explode('_', $key); 
      foreach($pieces AS $piece_key => $piece_value) { 
       $pieces[$piece_key] = ucfirst($piece_value); 
      } 

      $name = implode('',$pieces); 
      $method = 'set' . $name; 
      //$method = 'set' . ucfirst($key); 
      if (in_array($method, $methods)) { 
       $this->$method($value); 
      } 
     } 
     return $this; 
    } 

    //I added this method. So that I could "inject"/set the $_twitter obj 
    public function setTwitter($obj) 
    { 
     $this->_twitter = $obj; 
     return $this; 
    } 


    public function verifyCredentials() 
    { 
     return $this->_twitter->account->verifyCredentials(); 
    } 

    public function friendsTimeline($params) 
    { 
     return $this->_twitter->status->friendsTimeline($params); 
    } 
    //in the real code, more will go here... 
} 

而且在我的單元測試,我有這樣的:

<?php 
// tests/application/models/TwitterTest.php 
require_once ('../application/models/Twitter.php'); 

class Model_TwitterTest extends ControllerTestCase 
{ 
    public function testVerifyCredentials() 
    { 
     $stub = $this->getMock('Zend_Service_Twitter', array('verifyCredentials'),array(),'',FALSE); 
     //FALSE is actually the 5th parameter to flag getMock not to call the main class. See Docs for this. 
     //Now that I have set the $_twitter variable to use the mock, it will not call the main class - Zend_Rest_Client (i think) 

     $stub->expects($this->once()) 
       ->method('verifyCredentials'); 

     $model = new Twitter_Model_Twitter(); 

     //this is the part where i set the $_twitter variable in my model to use the $stub 
     $model->setOptions(array('twitter'=>$stub)); 

     $model->verifyCredentials(); 
    } 
} 

不管怎麼說,我覺得我得到它的工作。

1)單元測試不再試圖連接到twitter.com:80

2)後,我得到了setOptions()在Twitter_Model工作,$模型 - >的verifyCredentials()在我的單元測試是成功呼叫。

我會等待其他人在Stackoverflow來確認這是正確的答案。與此同時,想要聽到你們的聲音。

謝謝!

相關問題