【メモ】Laravelでテスト中にこんなエラーが出た

原因がわからなくてハマりました






エラーはこんな感じ

TypeError: htmlspecialchars(): Argument #1 ($string) must be of type string, array given in /var/www/html/vendor/laravel/framework/src/Illuminate/Support/helpers.php:123


エラーの内容を素直に解釈するとbladeテンプレートで配列を直接書きだそうとしているよということなんですが、問題は、ブラウザで確認してもこのエラーは出ないと言うこと。ユニットテストを回したときだけエラーになってテストが失敗します。どの部分が原因なのかわからず、結局1つずつ削除しながら確認したところ、次の書き出しが問題のようでした。


{{ __('Questions') }}


これは「質問」というページの見出し部分で、多言語化対応のめ __() 関数を通して書き出しています。で、言語を日本語にして見るとそのまま「質問」と表示されるのですが、これの何が問題なのか?



他のメンバーが「questions.php」という言語ファイルを作ってた

チーム内ルールとして言語ファイルは基本的に「lang/ja.json」だけを使うことになっていたのですが、あるメンバーが誤って「lang/ja/questions.php」というファイルを作っていたことがわかりました。Laravelのドキュメントによると、ファイル名と翻訳文字列キーは競合するので同じ文字列を定義しないようにとあります。


キー/ファイルの競合
他の翻訳ファイル名と競合する翻訳文字列キーを定義しないでください。たとえば、nl/action.phpファイルは存在するがnl.jsonファイルは存在しないときに、”NL”ロケールの__(‘Action’)を翻訳すると、トランスレータはnl/action.phpの内容を返します。

多言語化 9.x Laravel


つまり「lang/jp/questions.php」があると {{ __('questions') }} は「lang/jp/questions.php」の中身を展開してしまうということみたいですね。チームメンバーが「lang/jp/questions.php」に記述していた内容を「lang/ja.json」に移動し、「lang/jp/questions.php」を削除することで問題は解決しました。



ポイント:ユニットテストでは大文字と小文字を区別しない

というわけで問題は解決したのですが、もうひとつポイントとして、


ブラウザで表示するときは大文字と小文字を区別するが、ユニットテストでは区別しない


というものがあるようです。「lang/jp/questions.php」があるとき、ブラウザなら {{ __('Questions') }} は問題を起こさないけれど、ユニットテストでは問題になるという。



どちらが正解でも良いんですけど、挙動が変わるのはちょっと嫌だなあ。そんなことされたらそりゃハマりますよね。わかってよかった。