2013-11-07 2 views
5

У меня был небольшой тест сделано в PHP для контроллера я написал в Symfony2:PHPUnit: Тестирование формы представления с сессионных переменных, хранящихся в 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; 
    } 
} 

Так что я должен был mofidy контроллер, чтобы получить временное значение при отображении формы, и потребляют его при подаче.

Но теперь это представляет проблему. Я не могу сделать запрос к POST /deposit, потому что я не знаю, что отправить nonce. Я подумал просить сначала GET /deposit визуализировать форму и установить ее для использования в POST, но я подозреваю, что сеансы Symfony2 не работают в PHPUnit.

Как я могу решить эту проблему? Я бы не захотел пойти на тесты Selenium, так как они значительно медленнее, не говоря уже о том, что мне пришлось бы переписать много тестов.

UPDATE: Я добавляю очень упрощенную версию кода контроллера по запросу.

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

Не могли бы вы показать свой код контроллера? Что еще делает ReplayManager? Я думаю, что ваша проблема здесь заключается в разделении проблем, и ваш код контроллера может быть полезен для подтверждения этого. – vascowhite

+0

Done, я написал очень упрощенную версию контроллера –

+0

Почему бы не использовать [PRG pattern] (http://en.wikipedia.org/wiki/Post/Redirect/Get)? – sectus

ответ

1

Я не уверен, что делает ReplayManager, но он смотрит на меня, как будто это не правильный класс для обработки «» одноразового номера. Поскольку «nonce» в конечном счете хранится и извлекается из сеанса, он должен либо обрабатываться контроллером, либо абстрагироваться от его собственного класса, который затем передается как зависимость. Это позволит вам высмеивать nonce (звучит как комедия!) Для тестирования.

В моем опыте проблемы с тестированием на самом деле являются проблемами с дизайном кода и должны рассматриваться как запах. В этом случае ваша проблема связана с обработкой nonce в неправильном месте. Быстрый сеанс рефакторинга должен решить ваши проблемы с тестированием.

+0

Согласовано - насколько мне известно, все, что требует объект сеанса, должно обрабатываться в контроллере. Тем не менее, я не думаю, что это вносит вклад в проблему OP в этом случае. – redbirdo

+0

Ну, replayManager обрабатывается контроллером, так разве это не то же самое? Я обновил контроллер, чтобы показать, как я проходил сессию. –

0

Доступ к сеансу Symfony2 возможен из PHPUnit через клиента WebTestCase. Я думаю, что что-то, как это должно работать:

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()); 
} 

EDIT:

В качестве альтернативы, если существует проблема получения нонса значения из сессии, можно попытаться заменить две линии между GET и POST запросов выше с:

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

Я пробовал это!Проблема в том, что когда я делаю 'POST', то nonce, которое было создано на первой странице, не находится на второй странице. Это то, что я имел в виду, когда PHP-сессии не работали на PHPUnit –

+0

Хммм. Я успешно извлекаю значение сеанса в модульном тесте, но у меня также есть комментарий против него. // Если мы получим это позже, оно пусто! " поэтому возникла проблема. Сказав это, этот проект по-прежнему находится на Symfony 2.0.4, поэтому ситуация, вероятно, изменилась - я надеялся на лучшее, но, возможно, нет? – redbirdo

+0

Я отредактировал, чтобы предложить альтернативный подход. – redbirdo

Смежные вопросы