「テストはコストをかけてやるべきではないよね」が主流だった

古川陽介氏(以下、古川):今までE2Eとかでやっていた時、もしかしたら倉見さんにも同じようなことが起きていましたか? 実際どうですか?

倉見洋輔氏(以下、倉見):そうですね、先ほど和田さんがおっしゃったところは、JavaでJSPで画面書いていた新卒から5年ぐらいの間、たぶん僕も含め、みんな同じようなことを考えていました。

フロントのテストというのは、とかく壊れやすい。それも、何が正という状態も、要件が変わると壊れてしまうので、そういった意味でも壊れやすい。なので、「コストをかけてやるべきではないよね」というのが主流だったというのは、肌感としてもあります。

例えば画面を通してやらなきゃいけないE2Eみたいなものも、なるべくそういう負荷を行わないように、最小限に済むようにしましょうというのがベストプラクティスというか、扱われていて、テストピラミッドみたいなものでとか、たぶんそういう話だと思うんですけどね。やはりそれがけっこう、感覚としてありました。

あと、僕はけっこう外でしゃべっている時も、やはり「コストかかりますよね」って。

古川:言われる?

倉見:言われますね。「倉見さんのところはやっているかもしれないけど、それってすごくコストをかけてやっているだけでしょう」みたいな意味合いで言われます。「そんなことはないんだよ」と言いたい自分がいる時もけっこうありますね。

古川:あぁ、そうですね。ちょっとお話を元に戻すと、確かに「Viewのテストを書きましょう」という時に、単体でViewをテストする仕組みが整理されてこなかった中で、おそらくE2Eみたいにすべてのサービスをいったん組み上がった状態にしてから、外側からテストするというやり方を取ろうとすると、どうしてもビルドのタイミングになってしまう。

あと、ブラウザを模したヘッドレスブラウザだったり、ブラウザそのものを使ってE2Eテストをすることも多いですが、そうするとやはり依存関係が複雑になりすぎて、「タイミングによってはダメというのが起きるから、謎のスリープを30秒入れておこう」みたいなコードが増えてきちゃう。

結局、メンテナンス性も再現性も悪いテストコードになってしまう。結果、コストが高いという印象を植え付けられてしまっていたところがあるのかなという話ですね。

つまり、やり方もあまり良くなかったし、それに加えて、そのやり方がもたらしていたメンテナンス性や再現性も良くなかったから、結果、トータルとして費用対効果が悪いものというふうに印象付けられていたのが、たぶん歴史上の話ですね。

フロントエンドでのテストが書けるようになったターニングポイントは?

古川:じゃあ今はどうなのだろうか? というところですよね。過去はそうでしたが、現在はどうか? というと、フロントエンドテストを書くのがけっこう普通になってきたかなという印象があります。

この5年から最近に関して言うと、わりといろいろなことが普通になってきたなと思います。何がターニングポイントだったのか、今までずっとテストをやられてきた和田さんと、やっている倉見さんの2人の意見を聞いてみようかなと思います。これ、何がターニングポイントだったと思いますか?

和田卓人氏(以下、和田):ターニングポイント。フロントエンドでのテストがまあまあ書けるようになってきたターニングポイントは、そもそもで言うと、フロントエンドの分離。フロントエンドという領域の出現。

それより前のテストが書かれていなかった頃って、サーバーサイドでDOMの断片が作られて、それに対してjQueryで動きを与えるとか、アプリケーションっぽい構造を導入するとか、そういったかたちでした。そもそもバックエンド、サーバーサイドがなければDOMが出来上がらなくて、テストもできないみたいな。

つまり、テスト容易性が低かったんですよね。テストが簡単に動く仕組みがなかった。テストって「書きやすさ」だけではなくて、「動かしやすさ」も大事で、「バックエンドを立ち上げておかないとテストが動かないんですよ」と言うと、みんなテストをしなくなってしまう。

ターニングポイントは、フロントエンドにロジック、仕組みがじわじわじわじわと寄ってきた時。その結果、フロントエンド側が独立して描画などを扱うようになりましたよね。DOMの断片の構築がサーバーサイドで行われていたのが、そうではなくてデータのかたちでフロントエンドにやってきて、フロントエンド側で複雑な画面も含めて、構築・描画されるようになってきた。

簡単に言うと、データが用意できれば画面は作れる。バックエンドがなくてもデータがあれば画面が再現できる。ということは、テストが動かせるという話になるので、じわじわじわじわと責務がフロントエンドのほうに寄っていって、フロントエンドとバックエンドの責任分界点が……一時期、HTMLみたいなやり取りをしていたのが、JSONをやり取りするところに収束してきましたね。

テストで「ピンチポイント」と言うんですけど。道が細くなったポイントがあって、ここをギュッと押さえておけば、戦略上……戦争の話で言うと、狭い谷があって、そこを押さえておけば、全体の首根っこをつかまえることができるみたいな。テストの自動化においては、アーキテクチャ上のそういうポイントを押さえるというのがとても大事です。

ギュッと狭くなった首根っこを押さえれば、サーバーサイド、バックエンド、フロントエンドを並列で開発できる。その後はスキーマの話になってくるんだけど、やり取りのルールだけを決めておいて、データとしてはある程度独立したかたちで開発できるようになったら、テストが書きやすくなったし、データを用意すればよくなった。

もう1つ、複雑なロジックもフロントエンドに寄っていったので、テストを書く動機も生まれたんですよね。なにかをいじるたびに、なにかがぶっ壊れるというのをひたすら目で確認するとなると、やはり人はだんだん疲れてきたり、飽きてきたり、モチベーションが落ちたり、成果も落ちたりします。そこで、これは自動化しないと(となった)。

まさにコスパの話で言うと、自動化がコスパとしてペイするようになってきたんじゃないかというのがあります。それとほぼ時代を同じくして、Reactとかも出てきて、いよいよフロントエンド側を独立させてテストが書きやすくなってきた。

やはり、バックエンドとフロントエンドの責務のバランスが、ややフロントエンドに寄った結果、アーキテクチャ上で重要なポイントを押さえれば、どちらもテストを書いて開発を進められるようになった時代になったことが、ターニングポイントだったなと思っています。

古川:すごくわかりやすい話。

和田:DOMスクリプティングの時代からアプリを作る時代になったからだと思っています。

古川:なるほど。

バックエンドとフロントエンドが、アーキテクチャ上、疎結合になってきている

古川:私なりの言葉でもう1度話すと、今、バックエンドとフロントエンドと呼ばれているアーキテクチャが2つあります。今までは1つのモノリシックなもので、その時に出てくる最終的なアウトプットはHTMLと呼ばれるかたちで出てきていました。

それは、人間の目やブラウザからすると扱いやすい単位だったけれども、コンピューター自体がチェックするのにあまり向いているフォーマットではなかった。全体がモノリシックであるがゆえに、それ自体があまりワークしてこなかった。

そこで、マイクロサービスとか、バックエンドとフロントエンドでHTMLそのものを作るのではなく、データを受け渡しして、データからHTMLを作る部分、つまり表示をする部分がフロントエンドの役割・責務であって、データを作るところまでがバックエンドの責務というふうに切り分けをした。

その切り分けをすることによって、先ほどのナローポイントと呼ばれている、首根っこをつかまえることができるようになってきた。データを出力するところだけを見ておけばバックエンドのテストができるし、フロントエンドは、その出力されたデータを今度は逆に入力として捉えて、その入力があったらこういうHTMLが返ってくるとしちゃえば、全体のシステムが要らないのでE2Eまでする必要もない。

そうすると逆に、ちょっとフレーキーな安定性の低いテストも、データを用意するところだけは確実にできるから、そこから先はやり方が少しシンプルになってきている。

かつ、フロントエンドがReactとか、Vueのライブラリとか進化してきたことによって、Reactは特に、特定のプロパティを与えたら特定のReactのコンポーネントのオブジェクトが返すので、ある程度それはそれで、HTMLそのものよりかは、コンピューターとして、JavaScript内で扱いやすいものになっていって、というところですね。

なんかすごく、俺だけずっとしゃべっていたけど(笑)、言いたかったことはおそらくそういうことで、バックエンドとフロントエンドそれぞれが、アーキテクチャ上、疎結合になってきているということですね。

和田:そうです。

古川:疎結合になってきたことによってインとアウトをうまく利用することで、その部分部分でテストが可能になってきたというお話だったかなと思います。

倉見氏が感じたテストにおけるターニングポイント

古川:ちなみにここは、いろいろなものがあると言っていたんですが、特にReactとかJestとかで言った時に、倉見さんもけっこうずっと前からやっていたのかなと思います。そのへん、明らかにやりやすくなった感じってありますか? テスティングライブラリとか出てきてからまたちょっと話が進化するのかなと。

倉見:たぶん、いろいろなテーマを内包しているところだとは思うんですが、ちょっと今までのお二人のところから引っ張って、先ほど古川さんが言ってくれたReactを例に取ります。

ナローポイントから降ってくるJSONのデータを与えたらHTMLを作ってくれる、そういう関数だという捉え方ができることによって、インが決まればアウトが決まる、ほかの副作用は持たない。これはたぶん、テストの中において一番テストが書きやすいパターンの1つですよね。

なので、単体テストのノウハウがすべて使えることに気づいた人たちがいろいろなツールを拡充し出した。たぶんテスティングライブラリとかも、その一環で整備されだしたものの部分だと僕は理解しています。

あるJSONを与えると、あるHTMLが返ってくる。これを検証する。これがユニットテストの基本なんですが、ポイントになるのは、「検証する」というところだと思っています。HTMLを検証するコードをテストコードとして人が書くということが、実はけっこう大変だということにその次に気づくんですよ、みんな。

ここで、Jestの人たち……最初にたぶん考えとしてあったのは別にJestではないと思うんですけども。彼らが用意したのが、初めてテストを書いた時の結果を正しいものとして、そのHTMLの文字列同士を次回の検証時に使いましょうというアプローチ。

これは、よく言われる「スナップショットテスティング」。別にフロントエンドだけのものではないのですが、これを使えば、とても面倒くさかったHTML部分の検証が、かなり低コストでできるようになるぞと。

入力の部分は、先ほど和田さんがおっしゃってくれたとおりで、ナローポイントから降ってくるデータをしっかり押さえておけば、フレーキーさもなくできる。この状態をReactで見せつけられた時に、「おっ、これは来たな」って(思いました)。

古川:そうですね。「来たな」と思ったと。

倉見:そうですね。

(次回へつづく)