繁体   English   中英

PHPUnit:使用存储在Symfony2中的会话变量测试表单提交

[英]PHPUnit: Testing form submissions with session variables stored in Symfony2

我在PHP中为我在Symfony2中编写的Controller做了一个小测试:

class DepositControllerTest extends WebTestCase {

    public function testDepositSucceeds() {

        $this->crawler = self::$client->request(
            'POST',
            '/deposit',
            array( "amount" => 23),
            array(),
            array()
        );

        $this->assertEquals(
            "Deposit Confirmation",
            $this->crawler->filter("title")->text());
    }
}

到目前为止,一切都很棒。 当我意识到我想在刷新页面时禁用可能的重新提交时问题就出现了。 所以我添加了一个小机制来在每次提交时发送nonce。

它的工作原理如下:

class ReplayManager {

    public function getNonce() {
        $uid = $this->getRandomUID();
        $this->session->set("nonce", $uid);
        return $uid;
    }

    public function checkNonce($cnonce) {

        $nonce = $this->session->get("nonce");

        if ($cnonce !== $nonce)
            return false;

        $this->session->set("nonce", null);
        return true;
    }
}

因此,我必须在控制器上进行操作,以便在显示表单时获取nonce,并在提交时使用它。

但现在这引入了一个问题。 我无法提出POST /deposit请求,因为我不知道要发送什么nonce。 我想请求第一个GET /deposit来呈现表单,并设置一个,在POST使用它,但我怀疑Symfony2会话在PHPUnit中不起作用。

我怎么能解决这个问题? 我不想去Selenium测试,因为它们显着变慢,更不用说我将不得不重写大量的测试。

更新:我按要求添加了一个非常简化的控制器代码版本。

class DepositController extends Controller{

    public function formAction(Request $request){

        $this->replayManager = $this->getReplayManager();
        $context["nonce"] = $this->replayManager->getNonce();

        return $this->renderTemplate("form.twig", $context);
    }

    protected function depositAction(){

        $this->replayManager = $this->getReplayManager();
        $nonce               = $_POST["nonce"];

        if (!$this->replayManager->checkNonce($nonce))
            return $this->renderErrorTemplate("Nonce expired!");

        deposit($_POST["amount"]);

        return $this->renderTemplate('confirmation.twig');
    }

    protected function getSession() {
        $session = $this->get('session');
        $session->start();
        return $session;
    }

    protected function getReplayManager() {
        return new ReplayManager($this->getSession());
    }

}

我不确定ReplayManager做了什么,但它看起来好像它不是处理'nonce'的正确类。 由于'nonce'最终存储在会话中并从会话中检索,它应该由控制器处理或者抽象到它自己的类中,然后作为依赖项传入。 这将允许您模拟nonce(听起来像情景喜剧!)进行测试。

根据我的经验,测试中的问题实际上是代码设计的问题,应该被认为是一种气味。 在这种情况下,您的问题源于在错误的地方处理nonce。 快速重构会话应该可以解决您的测试问题。

可以通过WebTestCase客户端从PHPUnit访问Symfony2会话。 我认为这样的事情应该有效:

public function testDepositSucceeds() {

    $this->crawler = self::$client->request(
        'GET',
        '/deposit',
    );

    $session = $this->client->getContainer()->get('session');
    $nonce = $session->get('nonce');

    $this->crawler = self::$client->request(
        'POST',
        '/deposit',
        array("amount" => 23, "nonce" => $nonce),
        array(),
        array()
    );

    $this->assertEquals(
        "Deposit Confirmation",
        $this->crawler->filter("title")->text());
}

编辑:

或者,如果从会话中获取nonce值时出现问题,您可以尝试将上面的GET和POST请求之间的两行替换为:

$form = $crawler->selectButton('submit');
$nonce = $form->get('nonce')->getValue(); // replace 'nonce' with the actual name of the element

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM