phpformssymfonyfunctional-testingdomcrawler

How to test form submission with wrong values using Symfony crawler component and PHPUnit?


When you're using the app through the browser, you send a bad value, the system checks for errors in the form, and if something goes wrong (it does in this case), it redirects with a default error message written below the incriminated field.

This is the behaviour I am trying to assert with my test case, but I came accross an \InvalidArgumentException I was not expecting.

I am using the symfony/phpunit-bridge with phpunit/phpunit v8.5.23 and symfony/dom-crawler v5.3.7. Here's a sample of what it looks like :

public function testPayloadNotRespectingFieldLimits(): void
{
    $client = static::createClient();

    /** @var SomeRepository $repo */
    $repo = self::getContainer()->get(SomeRepository::class);
    $countEntries = $repo->count([]);
    
    $crawler = $client->request(
        'GET',
        '/route/to/form/add'
    );
    $this->assertResponseIsSuccessful(); // Goes ok.

    $form = $crawler->filter('[type=submit]')->form(); // It does retrieve my form node.
    
    // This is where it's not working.
    $form->setValues([
        'some[name]' => 'Someokvalue',
        'some[color]' => 'SomeNOTOKValue', // It is a ChoiceType with limited values, where 'SomeNOTOKValue' does not belong. This is the line that throws an \InvalidArgumentException.
    )];

    // What I'd like to assert after this
    $client->submit($form);
    $this->assertResponseRedirects();
    $this->assertEquals($countEntries, $repo->count([]));
}

Here's the exception message I get :

InvalidArgumentException: Input "some[color]" cannot take "SomeNOTOKValue" as a value (possible values: "red", "pink", "purple", "white").
vendor/symfony/dom-crawler/Field/ChoiceFormField.php:140
vendor/symfony/dom-crawler/FormFieldRegistry.php:113
vendor/symfony/dom-crawler/Form.php:75

The ColorChoiceType tested here is pretty standard :

public function configureOptions(OptionsResolver $resolver): void
{
    $resolver->setDefaults([
        'choices' => ColorEnumType::getChoices(),
        'multiple' => false,
    )];
}

What I can do, is to wrap in a try-catch block, the line where it sets the wrong value. And it would indeed submit the form and proceed to the next assertion. The issue here is that the form was considered submitted and valid, it forced an appropriate value for the color field (the first choice of the enum set). This is not what I get when I try this in my browser (cf. the intro).

// ...
/** @var SomeRepository $repo */
$repo = self::getContainer()->get(SomeRepository::class);
$countEntries = $repo->count([]); // Gives 0.
// ...
try {
    $form->setValues([
        'some[name]' => 'Someokvalue',
        'some[color]' => 'SomeNOTOKValue',
    ]);
} catch (\InvalidArgumentException $e) {}

$client->submit($form); // Now it submits the form.
$this->assertResponseRedirects(); // Ok.
$this->assertEquals($countEntries, $repo->count([])); // Failed asserting that 1 matches expected 0. !!

How can I mimic the browser behaviour in my test case and make asserts on it ?


Solution

  • It seems that you can disable validation on the DomCrawler\Form component. Based on the official documentation here.

    So doing this, now works as expected :

    $form = $crawler->filter('[type=submit]')->form()->disableValidation();
    $form->setValues([
        'some[name]' => 'Someokvalue',
        'some[color]' => 'SomeNOTOKValue',
    ];
    $client->submit($form);
    
    $this->assertEquals($entriesBefore, $repo->count([]); // Now passes.