1

编码

应用控制器.php

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

class AppController extends Controller {

    public $components = array(
        'Auth' => array(
            'authorize' => array('Controller')
        ),
        'Session'
    );
}

PostsController.php

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

class PostsController extends AppController {

    public function isAuthorized() {
        return $this->Auth->user('role') == 'admin';
    }

    public function add() {
        $this->set('some_var', true);
    }
}

PostsControllerTest.php

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

class PostsControllerTest extends ControllerTestCase {

    public function setUp() {
        parent::setUp();
        CakeSession::write('Auth.User', array(
            'id' => 2,
            'username' => 'joe_bloggs',
            'role' => 'user',
            'created' => '2013-05-17 10:00:00',
            'modified' => '2013-05-17 10:00:00'
        ));
    }

    public function testAddWhileLoggedInAsNonAdminFails() {
        $this->testAction('/posts/add/', array('method' => 'get'));
        $this->assertTrue($this->vars['some_var']);
    }

    public function tearDown() {
        parent::tearDown();
        CakeSession::destroy();
    }
}

问题

现在,“testAddWhileLoggedInAsNonAdminFails”测试通过了。它应该失败。问题是重定向不会退出/停止模拟请求。

部分解决方案

我可以通过修改“AppController.php”和“PostsControllerTest.php”来解决这个问题,如下所示:

修改 AppController.php

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

class AppController extends Controller {

    public $components = array(
        'Auth' => array(
            'authorize' => array('Controller'),
            // ***** THE FOLLOWING LINE IS NEW *****
            'unauthorizedRedirect' => false
        ),
        'Session'
    );
}

修改 PostsControllerTest.php

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

class PostsControllerTest extends ControllerTestCase {

    public function setUp() {
        parent::setUp();
        CakeSession::write('Auth.User', array(
            'id' => 2,
            'username' => 'joe_bloggs',
            'role' => 'user',
            'created' => '2013-05-17 10:00:00',
            'modified' => '2013-05-17 10:00:00'
        ));
    }

// ***** THE FOLLOWING 3 LINES ARE NEW *****
/**
 * @expectedException ForbiddenException
 */
    public function testAddWhileLoggedInAsNonAdminFails() {
        $this->testAction('/posts/add/', array('method' => 'get'));
    }

    public function tearDown() {
        parent::tearDown();
        CakeSession::destroy();
    }
}

该解决方案的问题在于它也修改了真实网站的行为。我正在寻找一种方法来将 Auth 组件的unauthorizedRedirect属性设置为false 在运行测试时。我怎样才能做到这一点?

4

2 回答 2

2

更改代码的行为以使测试正常工作并不是一个好主意。

这个问题的正确答案是这不是一个很好的问题,你真正应该做的是分别测试每个函数。

对于 isAuthorized 函数,您应该执行以下操作:

<?php
class PostsControllerTest extends ControllerTestCase {

    public function testIsAuthorized() {
        $Posts = $this->generate('Posts');
        $user = array('role' => 'admin');
        $this->assertTrue($Posts->isAuthorized($user));
        $anotherUser = array('role' => 'saboteur');
        $this->assertFalse($Posts->isAuthorized($user));
    }
    public function testAdd() {
        $this->testAction('/posts/add/', array('method' => 'get'));
        $this->assertTrue($this->vars['some_var']);
    }
}

单元测试背后的核心概念是将您的应用程序分解为尽可能小的部分,并单独测试每个部分。一旦你整理好你的单元测试,你就可以进行涵盖多个功能的集成测试,但是很多项目从来没有达到那个阶段,这没关系。处理重定向问题可能很有趣,但您可以按照此博客文章中的说明模拟 controller::redirect 。它有点旧,但仍然有用。

于 2013-06-14T20:57:50.553 回答
0

你查过书吗?http://book.cakephp.org/2.0/en/development/testing.html#testing-controllers

在测试包含 redirect() 和重定向后的其他代码的操作时,重定向时返回通常是一个好主意。这样做的原因是,redirect() 在测试中被模拟了,并且不会像往常一样退出。而不是您的代码退出,它将继续在重定向之后运行代码。

它准确地描述了您的问题。

我还没有测试过,但试试吧,手册说控制器在使用 ControllerTestCase 时已经被模拟了,所以你应该可以期待它:

$this->controller->expects($this->at(0))
    ->method('redirect')
    ->with('/your-expected-input');

查看 ControllerTestCase 类可能会揭示控制器是如何被精确模拟和设置的。或者,您可以回退到常规的 CakeTestCase 并自己设置控制器模拟。

另一种选择是扩展您要测试的控制器并覆盖 redirect() 方法,而不是调用父级,而是将第一个 arg 设置为像 Controller::$redirectUrl 这样的属性。在您的操作调用之后,您可以 assertEqual 属性值。但这仍然需要您在控制器中的重定向调用之后返回。这在使用 ControllerTestCase 时也不起作用,因为它会模拟您的覆盖方法。

于 2013-06-17T00:07:36.393 回答