3

私は、Yii アプリ開発で TDD を使用する最良の方法を常に探しています。現在、ほとんどの Web アプリは、フロントエンドの API レイヤー (通常は JSON) で構成され、サーバーとバックエンドへの非同期呼び出しを提供します。私の側では、このアプリで最もよく使用されるテストは、単体テストと機能テストです。後者は PHPUnit + Selenium を活用したガイドや書籍で最も広く示されていますが、Behat + Mink も非常にクールに思えます (ただし、まだあまり自信がありません)。

ブラウザー (Selenium など) を使用する機能テストを使用したことがある場合は、それらを実行する必要が少ないほど、気分が良くなることを知っています。これにより、速度が遅くなり、保守が難しくなり、時には (JS SDK を使用したポップアップ FB ログインのように) 苦痛を伴います。

単一の Web ページ アプリケーションを操作するときは、自分の API の JSON 出力をテストすることに関心があります。これらの機能を単体テストのようなアプローチでテストして、保守が容易な高速テストを実現したいと考えています。私のコントローラーのアクションのほとんどは、accessControl フィルターを使用してログに記録されたユーザーのみが利用できることを考慮して、テストを実行するための最良の方法を考えました。

現時点では、これを達成するには2つの方法があると思います

  • 目的のエンドポイントに対して cUrl を使用して、JSON を直接呼び出す
  • コントローラーの機能

最初のシナリオでは、フィクスチャを使用できますが、(ログに記録されたユーザーをエミュレートするために) CWebUser クラスをモックする方法がありません。cUrl が来たときに Apache を使用すると、PHPUnit によって実行されたものではない CWebApplication のインスタンスによって実行されます。すべての API 呼び出しをステートレスにし、その結果として accessControl フィルターを削除することで、この問題を取り除くことができます。

2 番目の例では、CWebUser クラスをモックする唯一の方法は、実行中のテスト クラスでオーバーライドすることです。このアプローチは、さまざまなタイプのユーザーを必要とするユースケースをテストする必要がなくなり、実行時 (またはセットアップ時) に Web ユーザーのモックを変更する方法がなくなるまで有効です。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 出力をテストするのは無駄ではないでしょうか?

よろしくお願いします

4

2 に答える 2

0

多分私はあなたの問題を単純化しすぎています。テストを実行する前に、ユーザー ログインをエミュレートしたいようですね。その場合、フィクスチャで User オブジェクトを作成し、テストを実行する前に実際にログインし、テストを実行した後にログアウトしないのはなぜですか?

何かのようなもの:

/**
 * 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);
}
于 2013-01-25T18:39:58.080 に答える
0

実際、私と私のチームが見つけた唯一の解決策は、スタブ WebUser クラスを作成することです。このように WebUser クラスを書き換えると、Yii がセッションに依存することなくユーザーを認証できます。

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

テスト クラスの setUp メソッドでは、任意のユーザーをログインできます (この場合は、フィクスチャを利用しています)。

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

最後に、テスト構成ファイルでユーザー コンポーネントをオーバーライドし、これを使用するように指示します。

保護された/config/test.php

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

これがそれを処理するための最良の方法であるかどうかはまだわかりませんが、うまくいくようです

于 2013-02-07T22:44:05.603 に答える