Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit: Testing form submissions with session variables stored in Symfony2

I had a small test done in PHP for a Controller I had written in 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());
    }
}

Up to here, everything was great. Problem started when I realized I wanted to disable possible re-submissions while refreshing the page. So I added a small mechanism to send nonce on every submission.

It works something like this:

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

So I had to mofidy the controller to get the nonce when displaying the form, and consume it when submitting.

But now this introduces a problem. I cant make a request to POST /deposit because I dont know what nonce to send. I thought to requesting first GET /deposit to render the form, and setting one, to use it in the POST, but I suspect Symfony2 sessions are not working in PHPUnit.

How could I solve this issue? I would not want to go to Selenium tests, since they are significant slower, not to mention that I would have to rewrite A LOT of tests.

UPDATE: I add a very simplified version of the controller code by request.

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

}
like image 649
Enrique Moreno Tent Avatar asked Nov 11 '22 18:11

Enrique Moreno Tent


1 Answers

I'm not sure what ReplayManager does, but it looks to me as if it is not the right class to handle the 'nonce'. As the 'nonce' is ultimately stored in and retrieved from the session it should either be handled by the controller or abstracted out into its own class which is then passed in as a dependency. This will allow you to mock the nonce (sounds like a sitcom!) for testing.

In my experience problems in testing are actually problems with code design and should be considered a smell. In this case your problem stems from handling the nonce in the wrong place. A quick refactoring session should solve your testing problems.

like image 147
vascowhite Avatar answered Nov 15 '22 05:11

vascowhite