正确模拟CakePHP控制器的引用()

时间:2014-06-20 04:36:51

标签: cakephp

我正在玩CakePHP,建立一个只有帖子和评论的简单博客网站。我烘焙了模型,控制器和视图。当您查看帖子时,它会显示"相关评论"底部的部分,显示了所有帖子的评论。每个评论都有操作按钮("查看","编辑"和"删除")。我注意到,如果您编辑或删除评论,则将被收回您正在查看的帖子。相反,您将被带到评论索引。当您查看帖子并点击"新评论时,会发生同样的事情。底部的按钮,并为该帖子添加新评论。我想修改此行为,以便在您查看帖子并编辑或删除现有评论或添加新评论时,您将重定向回帖子。我在Comments控制器中为这三个操作("添加","编辑"和"删除")中的每一个添加了代码。这个问题集中在"删除"行动。这就是我所做的:

app/Controller/CommentsController.php

中的原始代码
public function delete($id = null) {
    $this->Comment->id = $id;
    if (!$this->Comment->exists()) {
        throw new NotFoundException(__('Invalid comment'));
    }
    $this->request->allowMethod('post', 'delete');
    if ($this->Comment->delete()) {
        $this->Session->setFlash(__('The comment has been deleted.'));
    } else {
        $this->Session->setFlash(__('The comment could not be deleted. Please, try again.'));
    }
    return $this->redirect(array('action' => 'index'));
}

app/Controller/CommentsController.php

中的修改代码
public function delete($id = null) {
    /*
     * Same as above minus the redirect line (the following code replaces
     * the redirect line).
     */
    // Redirect to `$this->referer()` unless `$this->referer()`
    // is this comment's "edit" or "view" action (we do not want to
    // redirect the user to a 404 page), in which case redirect them
    // to the comments index.

    $referer = $this->referer();

    if ($referer) {
        $comingFromEditPage =
            mb_strpos(
                $referer,
                Router::url(array('action' => 'edit', $id), true)
            ) === 0;

        if (!$comingFromEditPage) {
            $comingFromViewPage =
                mb_strpos(
                    $referer,
                    Router::url(array('action' => 'view', $id), true)
                ) === 0;
        }

        if (!$comingFromEditPage && !$comingFromViewPage) {
            $redirectUrl = $referer;
        }
    }

    if (!isset($redirectUrl)) {
        $redirectUrl = array('action' => 'index');
    }

    return $this->redirect($redirectUrl);
}

我想在我的app/Test/Case/Controller/CommentsControllerTest.php文件中测试这种行为,所以我看起来像这样:

CommentsControllerTest.php

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

class CommentsControllerTest extends ControllerTestCase {

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

    public function tearDown() {
        parent::tearDown();
        unset($this->Controller);
    }

    public function testDeleteReferer() {
        // When the referer is the comment's "edit" action, deleting the
        // comment should redirect to the comments index.

        $this->Controller = $this->generate('Comments', array(
            'methods' => array(
                'referer'
            )
        ));

        $this->Controller->expects($this->any())->method('referer')->will(
            $this->returnValue(
                Configure::read('App.fullBaseUrl') .
                $this->Controller->request->base .
                Router::url(
                    array(
                        'controller' => 'comments',
                        'action' => 'edit',
                        '1',
                    )
                )
            )
        );

        $commentsIndexUrl =
            Configure::read('App.fullBaseUrl') .
            $this->Controller->request->base .
            Router::url(
                array(
                    'controller' => 'comments',
                    'action' => 'index',
                )
            );

        $this->testAction(
            '/comments/delete/1',
            array('method' => 'post')
        );

        $this->assertEqual(
            $this->headers['Location'],
            $commentsIndexUrl
        );

        // When the referer is the comment's "view" action, deleting the
        // comment should redirect to the comments index.

        $this->Controller = $this->generate('Comments', array(
            'methods' => array(
                'referer'
            )
        ));

        $this->Controller->expects($this->any())->method('referer')->will(
            $this->returnValue(
                Configure::read('App.fullBaseUrl') .
                $this->Controller->request->base .
                Router::url(
                    array(
                        'controller' => 'comments',
                        'action' => 'view',
                        '2',
                    )
                )
            )
        );

        $this->testAction(
            '/comments/delete/2',
            array('method' => 'post')
        );

        // The following assertion fails on the command line!

        $this->assertEqual(
            $this->headers['Location'],
            $commentsIndexUrl
        );
    }

}

如上面的代码所述,第二个assertEqual在命令行上失败。当我运行cake test --stderr app Controller/CommentsController时,这是输出:

Welcome to CakePHP v2.5.2 Console
---------------------------------------------------------------
App : app
Path: /Applications/MAMP/htdocs/blogtest/app/
---------------------------------------------------------------
CakePHP Test Shell
---------------------------------------------------------------
F

Time: 0 seconds, Memory: 18.50Mb

There was 1 failure:

1) CommentsControllerTest::testDeleteReferer
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'http://localhost/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/comments'
+'http://localhost/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/comments/view/2'

/Applications/MAMP/htdocs/blogtest/lib/Cake/TestSuite/CakeTestCase.php:552
/Applications/MAMP/htdocs/blogtest/app/Test/Case/Controller/CommentsControllerTest.php:93
/Applications/MAMP/htdocs/blogtest/lib/Cake/TestSuite/CakeTestCase.php:82
/Applications/MAMP/htdocs/blogtest/lib/Cake/TestSuite/CakeTestRunner.php:60
/Applications/MAMP/htdocs/blogtest/lib/Cake/TestSuite/CakeTestSuiteCommand.php:96
/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/Command/TestShell.php:274
/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/Command/TestShell.php:259
/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/Shell.php:440
/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/ShellDispatcher.php:207
/Applications/MAMP/htdocs/blogtest/lib/Cake/Console/ShellDispatcher.php:66

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

即使测试在浏览器中工作正常,也知道为什么会在命令行上发生这种情况?我该如何解决?

1 个答案:

答案 0 :(得分:1)

这并不能回答您的确切问题,但我相信您可以更快,更清晰地解决您的问题,并使其更容易理解和测试。

如果删除成功(以便不会导致404),则需要重定向到索引;如果失败则需要引用。

public function delete($id = null) {
    if ($this->Comment->delete($id)) { //If comment deletion succeeds
        return $this->redirect(array('action' => 'index'); //Redirect to index
    } 
    return $this->redirect($this->referer()); //Else redirect to referrer
}

请注意return将如何停止进一步执行,因此您实际上并不需要else。这段代码现在只有3条简单的行,只有一个缩进级别。

此外,我认为此代码几乎不需要任何测试,因为它基于经过严格测试的核心功能。您可以相信Model::delete()Controller::redirect()可以完成他们的工作,如果您信任那些您可以信任控制器方法也会按预期执行的人。我认为测试模型比控制器更重要(不是说控制器应该被排除在外,但我只是懒得去测试那些复杂或容易发生变化/错误的事情。)

因此,如果您仍然需要进入极端测试阶段,可以将$_SERVER['HTTP_REFERER']设置为您喜欢的任何内容(CakeRequest$this->referer()选为{{1}})并查看是否为{{1}}删除失败后重定向到的URL。