2013-01-22 69 views
3

我一直在尋找在Yii應用開發中使用TDD的最佳方式。現在大多數Web應用都由一個前端API層(通常爲JSON)組成,以向服務器和後端提供異步調用。就我而言,在這個應用程序中最常用的測試是單元測試和功能測試。後者在指南和書籍中使用最廣泛的PHPUnit + Selenium,但Behat + Mink看起來也很酷(但我還沒有真正有信心)在Yii應用中使用Restful Api的TDD最佳實踐

如果您曾經使用過使用瀏覽器的功能測試像Selenium),你知道越少你必須運行他們越好,你的感覺。這導致它們更慢,更難維護,並且有時(比如使用JS SDK的彈出式FB登錄)是痛苦的。

當使用單個網頁應用程序時,我關心測試我的apis的JSON輸出。我想用類似單元測試的方法來測試這些功能,以便更快地測試,以便於維護。考慮到我的控制器的大部分操作都可用於使用accessControl過濾器的僅記錄用戶,我想知道如何讓我的測試正常運行。

在這個時刻,我認爲有兩個方法來完成對所需enpoint得到JSON這

  • 使用捲曲直接調用
  • 控制器的功能

在第一個場景我可以使用fixtures,但是我沒有辦法模擬CWebUser類(模擬一個已記錄的用戶),當cUrl出現時使用Apache,它被我的CWebApplication的一個實例執行,它不是由PHPUnit執行的實例。我可以通過將所有API調用無狀態來消除此問題,並因此刪除accessControl過濾器。

在第二個中,我發現模擬CWebUser類的唯一方法是在我正在執行的測試類中覆蓋它。這種方法支付,直到我不需要測試需要不同類型用戶的用例,並且我無法在運行時(或在安裝時)更改我的webuser模擬。我發現模擬我的web用戶的唯一方法是你可以在下面找到的,這導致$ this-> getMock('WebUser')不會影響配置文件中定義的CWebApplication的WebUser(只讀)單例。

又來了一個具體的例子:

class UserControllerTest extends CDbTestCase 
{ 
    public $fixtures=array(
      /* NEEDED FIXTURES*/ 
    ); 

    public function testUserCanGetFavouriteStore() { 

      $controller = new UserController(1); 
      $result = json_decode($controller->actionAjaxGetFavStore());      
      $this->assertInternalType('array', $result->data);    

      $model = $result->data[0]; 
      $this->assertEquals($model->name, "Nome dello Store"); 

    } 
} 

class WebUser extends CWebUser { 

    public function getId() { 
     return 1; 
    } 
    public function getIsGuest() { 
      return false; 
    } 
}; 

我在想,如果能或者通過API密鑰或用戶/密碼組合與API接口進行身份驗證,可能是有用的。 如果我轉向幾乎無狀態的API集成,這很好,但是大多數時候我只有控制器的操作(只允許登錄用戶)返回Json數據來填充前端。

任何人都可以建議我一個更好的方法?也許這只是無用的測試這種JSON輸出?

問候

回答

0

也許我過於簡單化你的問題。聽起來你想在運行測試之前模擬用戶登錄?如果是這樣的話,爲什麼不在你的燈具中創建一個用戶對象,並在運行測試之前實際登錄它們,然後登出?

喜歡的東西:

/** 
* Sets up before each test method runs. 
* This mainly sets the base URL for the test application. 
*/ 
protected function setUp() 
{ 
    parent::setUp(); 

    // login as registered user 
    $loginForm = new UserLoginForm(); 
    $loginForm->email = USER_EMAIL; // use your fixture 
    $loginForm->password = USER_PASSWORD; // use your fixture 
    if(!$loginForm->login()) { 
     throw new Exception("Could not login in setup"); 
    } 
} 

protected function tearDown() 
{ 
    parent::tearDown(); 
    Yii::app()->user->logout(true); 
} 
+0

嗨Aj, 感謝您的回覆,這不能工作,因爲只要我使用標準的Cwebuser-> login()方法應用程序嘗試在會話吐出數據寫入標準輸出內容,這使得phpunit引發錯誤。 –

0

好實際上是我和我的團隊發現是創建一個存根WEBUSER類的唯一解決方案。 以這種方式重寫WebUser類,您可以在不依賴會話的情況下對用戶進行身份驗證。

class WebUserMock extends WebUser { 

public function login($identity,$duration=0) 
{ 
    $id=$identity->getId(); 
    $states=$identity->getPersistentStates(); 
    if($this->beforeLogin($id,$states,false)) 
    { 
     $this->changeIdentity($id,$identity->getName(),$states); 
     $duration = 0; 
     if($duration>0) 
     { 
      if($this->allowAutoLogin) 
       $this->saveToCookie($duration); 
      else 
       throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.', 
        array('{class}'=>get_class($this)))); 
     } 

     $this->afterLogin(false); 
    } 
    return !$this->getIsGuest(); 
} 

public function changeIdentity($id,$name,$states) 
{ 
    $this->setId($id); 
    $this->setName($name); 
    $this->loadIdentityStates($states); 
} 

// Load user model. 
protected function loadUser() { 
    $id = Yii::app()->user->id; 
     if ($id!==null) 
      $this->_model=User::model()->findByPk($id); 
    return $this->_model; 
} 
}; 

在您的測試類的設置方法,您可以登錄任何用戶(在這種情況下,利用我的燈具)

//a piece of your setUp() method.... 
$identity = new UserIdentity($this->users('User_2')->email, md5('demo'));    
$identity->authenticate();  
if($identity->errorCode===UserIdentity::ERROR_NONE)          
    Yii::app()->user->login($identity);    

至於做只是覆蓋在測試配置的用戶部件的最後一件事文件,並告訴他用這一個:

保護/配置/ test.php的

'user'=>array(
    'class' => 'application.tests.mock.WebUserMock', 
    'allowAutoLogin'=>false, 
), 

仍然不確定這是最好的方式來處理它,但似乎工作正常