2009-08-10 5 views
4

Je me suis lancé dans les tests unitaires avec Zend Framework. Je m'habitue aux autres choses qu'elle fournit mais j'ai du mal à comprendre les Objets Mock.Zend Framework: Comment tester un modèle à l'aide de Zend_Service_Twitter

Pour cet exemple, j'essaie d'utiliser un objet Mock pour tester mon modèle.

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

Pour mon test unitaire:

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

Comment décririez-vous tester les éléments suivants? 1) verifyCredentials() 2) friendsTimeline()

Merci, Wenbert

Répondre

1

Je vais répondre à cette question. Je pense que j'ai fait ce travail grâce à zomg de #zftalk.

Voici mon nouveau Twitter Modèle:

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

Et dans mon test unitaire, j'ai ceci:

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

Quoi qu'il en soit, je pense que je l'ai eu à travailler.

1) pas Le test unitaire essayé plus de se connecter à twitter.com:80

2) Après avoir obtenu setOptions() travaillant dans le Twitter_Model, $ model-> verifyCredentials() dans mon test unitaire était appelé avec succès.

Je vais attendre les autres dans Stackoverflow pour confirmer que c'est la bonne réponse. En attendant, j'aimerais avoir de vos nouvelles.

Merci !!!