【翻訳】自動テストをすべき5つの理由

最近まで知らなかったのですが、さかな検定(通称ととけん)という検定試験があり、すでに6回実施されているのだそうです。ご推察の通り、さかなクンさんが一枚噛んでいらっしゃるようです。

さて、さいきん仕事でテストを全く書かないチームにジョインしたのですが、やはりこのままではつらい未来しか見えないため、なんとかテストを書くチームに変えていこうとテストをバリバリ書く先輩と一緒に活動を始めつつあります。

ところがいざテストを書くモチベーションを話そうとしてみると、19歳でTDDに出会い初配属部署も「テストは呼吸であり」「テストは義務教育であり」「テスト書けて初めて法の下の平等と基本的人権が主張できる」環境で過ごしてきた今になって言葉に集めるのも骨が折れそうだったので、ちょうど見つけた短めでわかりやすい英語記事を紹介することにしました。
今回はそれを和訳したものを紹介します。

この度翻訳した記事はこちら。
5 advantages of automated testing vs. manual testing.


自動テストをすべき5つの理由

顧客によっては、なぜ自分たちのシステムが自動テストされるべきかと聞いてくるかもしれません。
まず基本的な事実として、システムの品質を担保するなら適切にテストされているべきで、手動テストおよび自動テストはともに不可欠なものです。
それでも、自動テストすることへの投資に関しては納得出来ない顧客もいるでしょう。
いわく、システムが比較的小さいならば手動テストで十分だし、そこに対して自動テストをすることは無駄でしかない、など。

最初のうちは確かにそうかもしれません。開発チームが1〜2人程度と小さめでコードの量も少ないなら、バグの修正なども比較的容易です。
しかし、しばらくしてシステムの複雑性が増すとそれもどんどん難しくなっていきます。
単に大きいシステムにはその分多くのバグがあるだろうというだけのものではありません。
バグ修正作業はまずは問題箇所の同定から始まるわけですが、システムが大きいと開発者は自分の担当範囲外にも発生するようなバグなど容易には検知できません(*1)。
手動テストにかかる時間は、(システムの大きさに対して)指数的に増加します。なので、システムの規模はすぐに手動テストだけでは信頼性が確保できない大きさになります。
その時になって初めて、直さなくてはいけない技術的負債を抱えていることを悟るでしょう。

これはとても高くつく負債です。

手動テスト

仮に、自動テストのされてないシステムを手動でテストしようとしているとしましょう。
開発中のシステムに対して、「ユーザがアカウントを新規作成できるか」というサインアップ機能のテストを(手動で)行うなら、どのようにするでしょう?
例えばFacebookのように、名前を入れて、メールアドレス、パスワード、誕生日や性別を入力し、…。この入力フォームをテストするには、フォームの全てに正しい値を手入力し、登録ボタンをクリックし、登録が完了したことを確認することになります。

まだあります。
今のは単にサインアップ機能のただ1つのシナリオをテストしただけです。他にも考えないといけないシナリオがあります。例えば、メールアドレスが既に使われていたら…、メールアドレスが無効だったら…、ユーザがファーストネームを入れ忘れていたら……。
やっとの思いで全部終えてから、ものすごく時間を使っていたことに気づくでしょう。
それでもまだサインアップ機能だけです。システムをデプロイする度に同様にテストしないといけないことは他にもあります。
システムが大きくなるにつれ、また新機能の提供が強く要求されるにつれて、このような時間のかかる手動テストはやらなくなっていくでしょう。

自動テスト

自動テストでもやることは同じですが、ユーザの行動は開発者によってコードの形で表現されます(テストコード)。
これによって、テストは各開発者のPCやテストサーバの上で実施することができます。
先ほどのサインアップの例では、テストコードの実装に1〜2時間、ひとたび書いてしまえば1〜2分で実行することができるでしょう(これは私のRuby on Railsの経験に基づく感覚で、場合によりけりですが)。
Seleniumなどのツールを使えば、開発者はボタンやリンクなどがブラウザ上で実際にはどうクリックされて、フォームにはどう入力され、どうJavaScriptが実行されたか…などを実際に見ることができます。その場合は画面に表示しないで実施するテスト(*2)よりは遅いでしょうが、手動でやるよりは遥かに速いです。
自動テストは、ユーザの行動に伴うシステムの挙動をテストする目的にとどまりません。
単体テストは、例えば、あるクラスのあるメソッドが正しい値を返すか…などといったソースコードレベルでの各部分の検証を行います。何かしら複雑な処理をするメソッドであれば、この方法でテストをしておくべきでしょう。
特にコーナーケースなどを考慮した十分なテストを書き、たとえメソッドが想定外の引数で呼ばれた場合などでも正しく挙動するよう検証するべきです。

単体テストに加えて、例えばモジュール間やクラス間での挙動などを検証する結合テストもあります。
他にもテストのレベルはいろいろあります。
システムが全体として要件に沿って動くかを検証する(*3)システムテスト、開発しているシステムとサードパーティのシステムとの結合を検証するSystem Integration Testingなどがあります。

自動テストをすべき5つの理由

自動テストは必ずしも各々のあるいは全ての問題を解決するものではありませんが、手動テストと比較して十分な利益をもたらすことでしょう。
具体的には特にどういったものがあるでしょうか。

1. 安くつく

同じような手動テスト作業を何度も繰り返す(テストする度にフォームに正しい値を入力して、……)よりも、最初にテストを書くだけで済むほうが、最終的に使う時間は短くすみます。
つまり、システムのライフサイクル全体としてのトータルコストについては、”同等の”手動テストを行うよりも安くつきます。

2. 速い

ひとたびがんばってテスト書けば、実行するのは手動より遥かに速くすみます。
大きいシステムを網羅的に手動テストするのは時間がかかり過ぎます。

3. 信頼性が高い

ひとたびテストを書いてしまえば、特定のテストケースの実行を忘れるなどということはありません。
一方で手動テストでは、テスト担当者は一部のテストケースの実施を単に忘れることもあるでしょうし、そもそもあえて意図的に実行しないこともあるかもしれません。

例えば「ここはもう繰り返しテストされてて今さらやんなくてもいい」と思ってしまったり(だいたい後になってユーザがバグを見つけるというオチ)。

4. 人的・技術的リスクの低減

開発者は転職することもあるでしょうし、開発を内製化にスイッチすることなどもあるでしょう。
いずれも開発に関わるメンツが変わることになるわけですが、自動テストがなければ、この引き継ぎは非常に面倒で危険な仕事です(*4)

自動テストがあれば、新しい人は自分の行う変更がシステムの他の部分にどう影響するかを心配し過ぎることなく開発に入ることができます。

加えて、自動テストからはシステムについて多くのことを学ぶことができます。
テストケースによっては開発者にとってドキュメントたりえます。
例えば、CucumberFMやSteakFMといったツールを使うと開発者やプロダクトオーナーはテストシナリオをドキュメントの形で得られます。

さらに、自動テストはリファクタリングに役立ちます。
例えばパフォーマンス改善のためにシステムの挙動を保ったままコードを書き換えるときなど、自動テストがあれば、アプリケーションコードを書き換えてはテストを実行し、変更がシステムの挙動を壊していないかを容易に検証することができ、大きな時間の節約となります。

5. パワフルで多用途

手動テストでは、10万人のユーザを作ってパフォーマンス要件を満たすか…などといったテストは事実上不可能です。
自動テストなら例えばその10万ユーザから何かしら統計的に数字を取る操作は2秒とかからないでしょう。
また、例えばユーザアカウントが作られて49週間経ったらおめでとうメールを送る…などといった機能を検証するなど「時空を超えた」テストは手動では行えませんが、自動テストではDB内の日時情報を書き換えるなどによって行うことができます。
手動テストで行える検証はシステムのUIそのものによって制約されますが、自動テストではそれを超えることができます。

自動テストを導入を決める前に考えないといけないこと

もちろんテスト自動化は全ての問題に対する究極の解ではありません。
自動テストも人が書くもので、人はミスを犯すものなので、テストコードが誤っていることだってあり得ます。
あるいは、手動であれば見つかった問題を自動テストでは見つけられないという場合もあります。

Eric Ries(@ericries)のThe Lean Startupに面白い例があります:デザイナが、UIの背景色が白であるにも関わらずあるボタンを白にしてしまった場合でも、自動テストは問題なく通過してしまうのです。

自動テストは基本的には大きな利益をもたらしますが、決して万能薬ではありません。

開発者に関して

自動テストはそれ自体コードなので、何らかのプラクティスに基づいて書くべきでしょう。
アプリケーションコードと同様に、類似した部分などを再利用することで作業量(およびコスト)を低減できます。
自動テストのもたらす利益はテストコードの品質よっても変わるため、開発チームがテストコードの質についても注意を払える必要があります。

コストに関して

自動テストによって、手動テストに対し全体として、また長期的運用においてコストを低減できると書きましたが、テストコードを書きながら機能を開発する作業そのものはテスト無しで行う場合に比べて10-50%、場合によっては100%以上のコストが増加します。
要はテストありで開発する方がコーディング時間がかかるのです。
また、テスト済みのある部分に手を加える場合はそのテストコードにも変更が必要で、手がかかるかもしれません。
コストに関しては、もう一点CI (continuous integration)サーバのようなインフラ面の考慮も必要です。

時間について

最後にもう一つ考えなければいけないのは、時間についてです。
チームの開発速度を一定とすると、自動テストを書きながらの開発は時間がかかるため、時間がクリティカルな(遅延に伴うコストが高い)場合はより困難を伴います。
では、納期に関して要求が強い場合にはどうすればいいでしょう?
理想的にはテストは各機能の実装の前(テスト駆動開発)またはすぐ後に書かれることが望ましく、それができなくともデプロイ(リリース)の前に行うべきです。
納期が本当に本当に押していてなおかつ品質はそこまで重要ではないのであれば、テストを書くのをデプロイ(リリース)の後にすることもあるかもしれません。
その場合でも、なるべくすみやかに足りてないテストを書くよう努力すべきです。

では、自動テストの恩恵に与りたいが、加えてとにかく早くリリースしないといけない場合はどうしたらよいでしょう。
おそらく、まず最重要な部分を中心に統合テスト(Integration test)した上でリリースし、その後残りの部分のテストを書くことに集中することが最善の策でしょう。
これらの戦略はいずれも、いわゆるトレードオフの関係をなします。

まとめ

品質を求めるなら、システムを正しくテストすることを強くお勧めします。
自動テストはこの点で大きく役立つでしょう。
時間を取りますしいわば前払い投資でもありますが、システムのライフサイクル全体における時間・コスト・作業量を見ると、元を取れます。
概して自動テストは手動テストよりも早く安くつき、また手動テストでは不可能な形での品質の担保が可能です。

たとえシステムが完全に自動テストでカバーされていても、UI表示など手動でテストすることはあるでしょう。
それでも、手動テストによっても自動テストで得られるような利益が享受でき、自動テストを手動テストで代替することができる……などとは思わないでください。


個人的な補足

※ここに述べるのは個人の見解云々

いわゆるQA的なテスト・品質担保ではないというお話

テストコードの形で書かれたテストが、そのままいわゆるQuality Assuranceとしてのテストの代替になるかというと、そうではありません。
(逆に、そうだと困ります。極端な話開発中常にQA担当者に横に座ってもらわないといけなくなります。かんべんして欲しいです。)

ここで言うテストの目的はどちらかと言うと、非常にざっくりとした表現をすると、開発者のため・開発そのものを進めていくためのテストだと考えています。
例えば記事中にあった「リファクタリングの助け」「デバッグの助け」「仕様理解の助け」などなどの示すように、それぞれの開発者がより自信を持って作業に当たれるようになることが自動テストのもたらす最大の利益です。
なので、例えば「ここの挙動をテストによって明確にしとかないとと自分や他人がそのコードを読んだり使ったり変更したりするときちょっと不安抱くかもな…」というところから積極的にテストを書いていくべきだと考えています。

一方で、支援ツールによってテストコードで表現されたテストケースをドキュメント化すればQAのレベルでも役に立つ資料たりえることは間違いないので、そのような視点でもテストケース・テストシナリオの設計ができると、さらに後々良いかもしれません。

自動テストならではのパワフルさの補足

自動テストによってもたらされる特有の利益の1つに、モック・スタブの話があるかと思います。
おそらく本文中では「5. パワフルで多用途」への補足という形になります。

開発しているシステムが他システム、例えばFacebook APIを叩くとしましょう。
手動テストだとどうしましょう?偽名でアカウント大量に作りましょうか?即垢BANされますよ?という話になってしまいます。
そこで、例えばRuby+RSpecでよく用いられるWebMockのように、「特定の外部へのWebリクエストに対して特定のレスポンスがあったことにする」ことで、実際にfacebookを叩きに行くことなくその上層のアプリケーションコードをテストできます。

とは言えFacebook APIの叩き方そのものが本当に正しいかどうかというレベルの検証は実際に叩きに行かないとわからないため、自分たちは開発環境にデプロイして探索的テストを行う…というようなことをしています(次の項も参照のこと)。ここはどうするべきなんでしょう、正直自分もアドバイスが欲しいです。

手動テストを100%代替するものでもない

記事中にも繰り返し明確に示されていたとおり、自動テストがあれば手動テストしなくて良いというものでは全くありません
特にWebアプリケーションに関して言うと、

  • UI表示上の確認
  • 実装した挙動そのものの妥当性(仕様レベルの確認)
  • — 動くモノが出て触って初めて気づく設計の不備などはあるあるなので、しっかりとテスト用環境にデプロイしある程度系統的に触ってテストしなければいけません。
  • 自動テストの設定下ではテストできないことの検証

—- 自動テストの設定下では読み込む環境変数や繋ぎに行く外部システムなどなどが変わるので、それに依存した問題がないことの確認。

この辺りの詳細は探索的テストについてもお調べください。
誤訳等ありましたらご指摘いただけたら対応させていただきます。

*1: 訳怪しい

*2: PhantomJSなどヘッドレスブラウザの意味?

*3: 訳怪しい

*4: facebookは新人に対し1日目からバグ取り課題を課すと話題になりましたが、自分の変更が他に与える影響がわからない中コードを変えるなど、テストがないと危険すぎてできたものではありませんね

2 thoughts on “【翻訳】自動テストをすべき5つの理由

  1. 翻訳ありがとうございます。読んでもらいやすくていい感じです。

    とりあえず注釈がついている分だけ試訳をくっつけときます。
    *1
    When the application expands, it is harder to identify the bugs that arise not only in the part of the system the developer is actually working on but also in the parts of the system they are not working on.
    アプリケーションが大きくなってくると、バグを見つけるのは難しくなります。システムにおいて、開発者が実際に作業している部分だけでなく、まったく作業を行っていない部分からもバグが出てくるようになるのですから。

    *2
    ユニットテストなどの、UIを含まないテストと比較してという話かなと。

    *3
    There are also more general testing levels: system testing to check if a fully integrated system meets the requirements or system integration testing focused on the integration between your application and third-parties’ systems.

    もっと全体的なテストレベルもあります。完全に統合されたシステムが、要求を満たしているかを確かめるシステムテスト、アプリケーションとサードパーティのシステムが統合に重点を置いたシステム統合テストなどです。

  2. > そこで、例えばRuby+RSpecでよく用いられるWebMockのように、「特定の外部へのWebリクエストに対して特定のレスポンスがあったことにする」ことで、実際にfacebookを叩きに行くことなくその上層のアプリケーションコードをテストできます。
    > とは言えFacebook APIの叩き方そのものが本当に正しいかどうかというレベルの検証は実際に叩きに行かないとわからないため、自分たちは開発環境にデプロイして探索的テストを行う…というようなことをしています(次の項も参照のこと)。ここはどうするべきなんでしょう、正直自分もアドバイスが欲しいです。

    http://mattn.kaoriya.net/software/lang/go/20161231001721.htm

    この手の方法でいけると思います。

コメントを残す

メールアドレスが公開されることはありません。