【问题标题】:TDD Best Practice In Using Restful Api in Yii application在 Yii 应用中使用 Restful Api 的 TDD 最佳实践
【发布时间】:2013-01-06 06:38:59
【问题描述】:

我一直在寻找在 Yii 应用开发中使用 TDD 的最佳方式。如今,大多数 Web 应用程序都由一个前端 API 层(通常是 JSON)组成,以提供对服务器和后端的异步调用。就我而言,这个应用程序中最常用的测试是单元测试和功能测试。后者在指南和书籍中最广泛地展示了利用 PHPUnit + Selenium,但 Behat + Mink 似乎也很酷(但我对它还不是很有信心)

如果您曾经使用过使用浏览器(如 Selenium)的功能测试,您就会知道运行它们的次数越少,您的感觉就越好。这导致它们更慢,更难维护,有时(如使用 JS SDK 的弹出式 FB 登录)很痛苦。

在使用单个网页应用程序时,我关心测试我的 api 的 JSON 输出。我想使用类似单元测试的方法来测试这些功能,以便更快地进行更易于维护的测试。考虑到我的 Controller 的大部分操作都对使用 accessControl 过滤器的 Logged only 用户可用,我想知道让我的测试启动和运行的最佳方法。

目前我认为有两种方法可以实现这一点

  • 使用 cUrl 指向所需的 enpoint 以获取 JSON 直接调用
  • 控制器的功能

在第一种情况下,我可以使用固定装置,但我无法使用 Apache 模拟 CWebUser 类(模拟登录用户),当 cUrl 出现时,它由我的 CWebApplication 实例执行,该实例不是由PHP 单元。我可以通过使我的所有 API 调用无状态并因此删除 accessControl 过滤器来解决这个问题。

在第二种方法中,我发现模拟 CWebUser 类的唯一方法是在我正在执行的测试类中覆盖它。在我不需要测试需要不同类型用户的用例之前,这种方法是有效的,而且我无法在运行时(或设置时)更改我的网络用户模拟。我发现模拟网络用户的唯一方法是您可以在下面找到的方法,这导致 $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 输出没用?

最好的问候

【问题讨论】:

    标签: api unit-testing yii tdd phpunit


    【解决方案1】:

    也许我把你的问题简单化了。听起来您想在运行测试之前模拟用户登录?如果是这样的话,为什么不在你的夹具中创建一个用户对象并在运行测试之前将它们实际登录,然后再将它们注销呢?

    类似:

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

    【讨论】:

    • 嗨,Aj,感谢您的回复,这无法正常工作,因为一旦我使用标准 CWebuser->login() 方法,应用程序就会尝试在会话中写入内容,在标准输出上吐出数据,这使得phpunit 引发错误。
    【解决方案2】:

    好的,实际上我和我的团队找到的唯一解决方案是创建一个存根 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-03-14
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 2014-02-01
      • 2014-02-24
      • 1970-01-01
      相关资源
      最近更新 更多