我在PHP中为我在Symfony2中编写的控制器做了一个小测试:
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());
}
}
答案 0 :(得分:1)
我不确定ReplayManager是做什么的,但它看起来好像它不是处理'nonce'的正确类。由于'nonce'最终存储在会话中并从会话中检索,它应该由控制器处理或者抽象到它自己的类中,然后作为依赖项传入。这将允许你模拟nonce(听起来像情景喜剧!)进行测试。
根据我的经验,测试中的问题实际上是代码设计的问题,应该被视为气味。在这种情况下,您的问题源于在错误的地方处理nonce。快速重构会话应该可以解决您的测试问题。
答案 1 :(得分:0)
可以通过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