【问题标题】:CakePHP Controller Testing: Mocking the Auth ComponentCakePHP 控制器测试:模拟 Auth 组件
【发布时间】:2013-05-03 03:01:56
【问题描述】:

情况

控制器代码

<?php
App::uses('AppController', 'Controller');

class PostsController extends AppController {

    public function isAuthorized() {
        return true;
    }

    public function edit($id = null) {
        $this->autoRender = false;

        if (!$this->Post->exists($id)) {
            throw new NotFoundException(__('Invalid post'));
        }

        if ($this->Post->find('first', array(
            'conditions' => array(
                'Post.id' => $id,
                'Post.user_id' => $this->Auth->user('id')
            )
        ))) {
            echo 'Username: ' . $this->Auth->user('username') . '<br>';
            echo 'Created: ' . $this->Auth->user('created') . '<br>';
            echo 'Modified: ' . $this->Auth->user('modified') . '<br>';
            echo 'All:';
            pr($this->Auth->user());
            echo 'Modified: ' . $this->Auth->user('modified') . '<br>';
        } else {
            echo 'Unauthorized.';
        }
    }
}

浏览器输出

Username: admin
Created: 2013-05-08 00:00:00
Modified: 2013-05-08 00:00:00
All:

Array
(
    [id] => 1
    [username] => admin
    [created] => 2013-05-08 00:00:00
    [modified] => 2013-05-08 00:00:00
)

Modified: 2013-05-08 00:00:00

测试代码

<?php
App::uses('PostsController', 'Controller');

class PostsControllerTest extends ControllerTestCase {

    public $fixtures = array(
        'app.post',
        'app.user'
    );

    public function testEdit() {
        $this->Controller = $this->generate('Posts', array(
            'components' => array(
                'Auth' => array('user')
            )
        ));

        $this->Controller->Auth->staticExpects($this->at(0))->method('user')->with('id')->will($this->returnValue(1));
        $this->Controller->Auth->staticExpects($this->at(1))->method('user')->with('username')->will($this->returnValue('admin'));
        $this->Controller->Auth->staticExpects($this->at(2))->method('user')->with('created')->will($this->returnValue('2013-05-08 00:00:00'));
        $this->Controller->Auth->staticExpects($this->at(3))->method('user')->with('modified')->will($this->returnValue('2013-05-08 00:00:00'));
        $this->Controller->Auth->staticExpects($this->at(4))->method('user')->will($this->returnValue(array(
            'id' => 1,
            'username' => 'admin',
            'created' => '2013-05-08 00:00:00',
            'modified' => '2013-05-08 00:00:00'
        )));

        $this->testAction('/posts/edit/1', array('method' => 'get'));
    }
}

测试输出

Username: admin
Created: 2013-05-08 00:00:00
Modified: 2013-05-08 00:00:00
All:

Array
(
    [id] => 1
    [username] => admin
    [created] => 2013-05-08 00:00:00
    [modified] => 2013-05-08 00:00:00
)

Modified: 

问题

这里其实存在三个问题:

  1. 测试代码非常重复。
  2. 测试输出中的第二个“Modified”行是空白的。它 应该是“2013-05-08 00:00:00”,就像浏览器的输出一样。
  3. 如果我要修改控制器代码,在“用户名”和“已创建”的 echoing 之间添加一行 echo 'Email: ' . $this-&gt;Auth-&gt;user('email') . '&lt;br&gt;';(仅作为示例),测试将失败并出现以下错误:@987654327 @。这是有道理的,因为 $this-&gt;at(1) 不再正确。

我的问题

我怎样才能以以下方式模拟 Auth 组件:(1) 不重复,(2) 导致测试输出与浏览器相同的内容,以及 (3) 允许我在任何地方添加 $this-&gt;Auth-&gt;user('foo') 代码而无需打破测试?

【问题讨论】:

    标签: cakephp testing


    【解决方案1】:

    在回答这个问题之前,我必须承认我没有使用 CakePHP 框架的经验。但是,我在将 PHPUnit 与 Symfony 框架结合使用方面有相当多的经验,并且遇到过类似的问题。解决您的问题:

    1. 查看我对第 3 点的回答

    2. 这样做的原因是您需要额外的...-&gt;staticExpects($this-&gt;at(5))... 语句来覆盖对 Auth->user() 的第 6 次调用。这些语句没有定义任何使用指定值调用 Auth->user() 时要返回的值。他们定义了例如对 Auth 对象的第二次调用必须是带有参数“username”的方法 user(),在这种情况下将返回“admin”。但是,如果您遵循下一点中的方法,这应该不再是问题。

    3. 我假设您在这里尝试实现的是独立于 Auth 组件测试您的控制器(因为坦率地说,测试控制器对用户对象进行一系列 getter 调用是没有意义的) .在这种情况下,一个模拟对象被设置为一个存根,以始终返回一组特定的结果,而不是期望具有特定参数的特定系列调用 (See PHP Manual entry on stubs)。这可以在你的代码中用'$this->any()'替换'$this->at(x)'。但是,虽然这将不需要添加我在第 2 点中提到的额外行,但您仍然需要重复。遵循在代码之前编写测试的 TDD 方法,我建议如下:

      public function testEdit() {
          $this->Controller = $this->generate('Posts', array(
              'components' => array(
                  'Auth' => array('user')
              )
          ));
              $this->Controller->Auth
                  ->staticExpects($this->any())
                  ->method('user')
                  ->will($this->returnValue(array(
                      'id' => 1,
                      'username' => 'admin',
                      'created' => '2013-05-08 00:00:00',
                      'modified' => '2013-05-08 00:00:00',
                      'email' => 'me@me.com',
                  )));
      
          $this->testAction('/posts/edit/1', array('method' => 'get'));
      }
      

    这将允许更新您的控制器,以便以任意顺序进行尽可能多的调用以获取用户属性只要模拟对象已经返回了这些属性。可以编写模拟对象以返回所有用户属性(或者可能所有可能与此控制器相关),而不管控制器是否以及多久检索它们。 (请注意,在您的特定示例中,如果您的模拟包含“电子邮件”,则控制器中的 pr() 语句将从测试中输出与浏览器不同的结果,但我假设您不希望能够向记录添加新属性无需更新您的测试)。

    以这种方式编写测试意味着您的控制器编辑功能需要是这样的 - 一个更可测试的版本:

    $this->autoRender = false;
    
    if (!$this->Post->exists($id)) {
        throw new NotFoundException(__('Invalid post'));
    }
    
    $user = $this->Auth->user();
    
    if ($this->Post->find('first', array(
        'conditions' => array(
            'Post.id' => $id,
            'Post.user_id' => Hash::get($user, 'id')
        )
    ))) {
        echo 'Username: ' . Hash::get($user, 'username') . '<br>';
        echo 'Created: ' . Hash::get($user, 'created') . '<br>';
        echo 'Modified: ' . Hash::get($user, 'modified') . '<br>';
        echo 'All:';
        pr($user);
        echo 'Modified: ' . Hash::get($user, 'modified') . '<br>';
    } else {
        echo 'Unauthorized.';
    }
    

    据我所知,Hash::get($record, $key) 是从记录中检索属性的正确 CakePHP 方法,尽管使用您在这里拥有的简单属性,我认为 user[$key] 可以正常工作也是。

    【讨论】:

      猜你喜欢
      • 2012-02-27
      • 2016-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多