更改CakePHP设置以进行测试

时间:2013-05-20 14:39:21

标签: cakephp testing

代码

AppController.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 。我怎么能这样做?

2 个答案:

答案 0 :(得分: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']);
    }
}

单元测试背后的核心概念是将您的应用程序分解为可能的最小部分,并单独测试每个部分。一旦你完成了单元测试,就可以进行涵盖多个功能的集成测试,但很多项目都没有达到那个阶段,这没关系。重定向问题可能很有意思,但您可以按照in this blog post所述模拟出controller :: redirect。它有点旧但仍然有用。

答案 1 :(得分: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的属性。在您的操作调用之后,您可以断言属性值。但是这仍然需要您在控制器中的重定向调用后返回。在使用ControllerTestCase时,这也不会起作用,因为它会模拟你的覆盖方法。