如何使用 Symfony 爬虫组件和 PHPUnit 测试表单提交的错误值?
当您通过浏览器使用该应用程序时,您发送了一个错误的值,系统会检查表单中的错误,如果出现问题(在本例中就是这样),它会重定向一条默认错误消息,该消息写在有罪的错误消息下方场地。
这是我试图用我的测试用例断言的行为,但我遇到了我没有预料到的 \InvalidArgumentException 。
我将 symfony/phpunit-bridge 与 phpunit/phpunit v8.5.23 和 symfony/dom-crawler v5.3.7 一起使用。 下面是它的示例:
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([]));
}
这是我收到的异常消息:
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
这里测试的 ColorChoiceType 非常标准:
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'choices' => ColorEnumType::getChoices(),
'multiple' => false,
)];
}
我能做的就是将其设置错误值的行包装在 try-catch 块中。它确实会提交表格并继续下一个断言。这里的问题是,表单被认为是已提交且有效,它强制为颜色字段(枚举集的第一个选择)提供适当的值。 当我在浏览器中尝试此操作时,这不是(参见简介)。
// ...
/** @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. !!
如何在测试用例中模仿浏览器行为并对其进行断言?
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 ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看来您可以禁用 DomCrawler\Form 组件上的验证。基于此处的官方文档。
所以这样做,现在可以按预期工作:
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 :