🎻

[Symfony] 機能テストでmultipleなファイル型フォームフィールドに複数のファイルをセットするには特殊な対応が必要という話

2020/12/16に公開

はじめに

Symfony Advent Calendar 2020 の16日目の記事です!🎄🌙

昨日は @smdhogehoge さんの N+1問題をFetch Modeで対策する でした✨

ちなみに、僕はよく TwitterにもSymfonyネタを呟いている ので、よろしければぜひ フォローしてやってください🕊🤲

本題

<input type="file" name="files" multiple> のような、 multiple 属性つきのファイル型フィールドを持つフォームの機能テストをしたいとします。

1つしかファイルをセットしない場合は特別なことをしなくても普通に動作するのですが、 複数ファイルをセットして送信したい場合は、実はちょっと特殊な対応が必要になります。

そのことについて解説します。

普通にやろうとするとどうなるか

普通にテストコードを書いてみるとこんな感じになると思います。

$crawler = $client->request('GET', '/foo');
$form = $crawler->filter('form')->form();

$client->submit($form, [
    'my_form[files]' => [
        new UploadedFile($pathToFile1, 'test1'),
        new UploadedFile($pathToFile2, 'test2'),
    ],
]);

しかし、これを実行すると

InvalidArgumentException: Unreachable field "1".

というエラーになります😓

解決方法

どうすればいいかというと、以下のように $form->set() で明示的に 2ファイル目用の FileFormField を追加してあげる ことで実行可能になります。

$crawler = $client->request('GET', '/foo');
$form = $crawler->filter('form')->form();

// Formオブジェクトに、2ファイル目用のFileFormFieldを明示的に追加
$node = $crawler->filter('form input[type="file"]')->getNode(0);
$form->set(new FileFormField($node));

$client->submit($form, [
    'my_form[files]' => [
        new UploadedFile($pathToFile1, 'test1'),
        new UploadedFile($pathToFile2, 'test2'),
    ],
]);

3ファイル以上セットしたい場合は、ファイルの数だけ FileFormField を追加する必要があります。

$crawler = $client->request('GET', '/foo');
$form = $crawler->filter('form')->form();

$node = $crawler->filter('form input[type="file"]')->getNode(0);
$form->set(new FileFormField($node)); // 2ファイル目用
$form->set(new FileFormField($node)); // 3ファイル目用
$form->set(new FileFormField($node)); // 4ファイル目用

$client->submit($form, [
    'my_form[files]' => [
        new UploadedFile($pathToFile1, 'test1'),
        new UploadedFile($pathToFile2, 'test2'),
        new UploadedFile($pathToFile3, 'test3'),
        new UploadedFile($pathToFile4, 'test4'),
    ],
]);

参考:forms - Symfony FileFormField - Testing (WebTestCase) multiple file upload - Stack Overflow

ちなみに

公式ドキュメント

// In the case of a multiple file upload
$form['my_form[field][0]']->upload('/path/to/lucas.jpg');
$form['my_form[field][1]']->upload('/path/to/lisa.jpg');

というサンプルコードがありますが、これも $form への UploadedFile の追加の手順が違うだけでやっていることは

$client->submit($form, [
    'my_form[field]' => [
        new UploadedFile('/path/to/lucas.jpg', 'lucas.jpg'),
        new UploadedFile('/path/to/lisa.jpg', 'lisa.jpg'),
    ],
]);

と同じなので、サンプルコードのとおりに書いたとしても、 FileFormField の追加をしておかないと、同じように

InvalidArgumentException: Unreachable field "1".

になります。

ドキュメントの更新が必要だと思うので、気力が湧いたらPR出そうと思います。誰か代わりに出してくれても全然ウェルカムです😂

おわりに

Symfonyの機能テストで、 <input type="file" multiple> なフォームフィールドに複数のファイルをセットするには特殊な対応( FileFormField の明示的な追加 )が必要という話を解説しました。

Web上にほとんど情報がない感じだったので自分は結構ハマりました😓
次の誰かがこの記事で救われることがあれば嬉しいです💪

Symfony Advent Calendar 2020、明日は seihmd さんです!お楽しみに!

GitHubで編集を提案

Discussion