2009-08-10 9 views
4

Ich bin in Unit Testing mit Zend Framework gekommen. Ich gewöhne mich an die anderen Dinge, die es zur Verfügung stellt, aber es fällt mir schwer, Mock Objects zu verstehen.Zend Framework: Wie man ein Modell mit Zend_Service_Twitter einheiten

In diesem Beispiel versuche ich ein Mock-Objekt zu verwenden, um mein Modell zu testen.

<?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); 
    } 
} 

Für meine Unit-Test:

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); 
    } 
} 

Wie würden Sie die folgende testen? 1) verifyCredentials() 2) friends()

Danke, Wenbert

Antwort

1

Ich werde diese Frage beantworten. Ich denke, ich habe diese Arbeit dank zomg von #zftalk gemacht.

Hier ist mein neues Twitter Modell:

<?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... 
} 

Und in meinem Unit-Test, ich habe diese:

<?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(); 
    } 
} 

Auch immer, ich glaube, ich habe es funktioniert.

1) Die Unit-Test nicht versucht, mehr zu verbinden twitter.com:80

2) Nachdem ich die setOptions bekam() arbeiten in der Twitter_Model, $ modell-> verifyCredentials() in meiner Unit-Test war erfolgreich angerufen.

Ich werde auf andere in Stackoverflow warten, um zu bestätigen, dass das die richtige Antwort ist. Für die Zwischenzeit möchte ich von euch hören.

Danke !!!