LoginSignup
155
176

More than 1 year has passed since last update.

【考察】テストコードのきれいな書き方

Posted at

作ったものが想定した動作をしているか。

それを確認するために、テスト(試験)を行います。
検証したいことがちゃんと実現できて確認が取れているのであれば、その品質自体は割と気にされないことが多い印象です。

保守・運用・追加開発 をしていくプロジェクトが多くあると思います。
その作業の中で、改善を取り入れていくこともあると思いますが、その中でも一番後回しにされるのが、テストコードの改善のように思います。
推測ですが、「コストによるメリット・リターンが少なすぎる」ことが理由かな…と(開発者目線ではリターンが大きいのですが、運用者目線ですとリターンが少なく見えてしまう)。

であれば、最初からある程度綺麗なものがどういうものかを考え、作成しておけば良いのではないか・・!

ということで、考察していきたいと思います。

前提

考察をするにあたり、言語化した時の表現や意味のズレが発生しやすい部分もあると思いますので、予め定義しておきたいと思います。

用語

  1. テスト
    • 作ったものが想定した動作をしているのかを確認すること
  2. 業務処理
    • テスト対象になるもの
    • 最終的に build され、ユーザーが触るもの
  3. テストコード
    • 業務処理の動作を担保するためのソースコード
    • 一つのトリガーで、何度でも同じ試験が実行可能なもの
  4. 動作確認
    • 実際に起動されているものを触って、業務処理の動作を確認・担保すること
  5. モック
    • ある処理に関して、決まった動作をするように設定をされているもののこと
    • 例. 「A の処理は、B を受け取ったらCを返却するはずなので、C を返却して」「A の処理は、 I を受け取ったらLを返却するはずなので、L を返却して」…と設定ができるもの
  6. スタブ
    • 想定されている動作になるように簡潔に作られているもののこと
    • 例. 「A の処理は、B を受け取ったら C, I を受け取ったら L を返却する」…と設定されているもの

触れない内容

  • テストコード と 動作確認 の線引き
  • テストの種類(単体テスト, 結合テスト...)
  • テストツール/テストライブラリ/言語による実装方法
  • テスト駆動開発
  • テストカバレッジ

考察

そもそも、「きれい」と言っているのは、「読みやすい」コード のことです。
「読みやすい」とは「将来の自分が見返した時。もしくは、少しだけ言語を知っている人が見た時、何をやっているのかを理解ができる」コードのことだと思います。

「業務処理」の「きれい」に関しては、適切な機能単位で切り分けられていたり、意義のあるコメントが残っているものかな…と思います。
業務処理に関しては皆さんも想像しやすい内容かと思いますが、「テストコード」の場合は同じではないのでは…と考えています。

AAAパターン

「AAA(Arrange、Act、Assert)パターン1」になっているもの。

がテストに関する学習を始めた時に、序盤で習得した内容でした。
ただ、 AAAパターンに倣えば全てOK!というわけではないと今では考えています。

過去に何度もやってしまったことですが、 複雑な内容になると Arrange 箇所が長くなり、 Assert 箇所が長くなり…。
同じようなコードがコピペで増え…。

という負の遺産2を作っていく作業になってしまいます。

なぜ AAAパターン?

なぜ、 「AAAパターンが良い」のかに関しては、理由があります。
それは、「 テストをするために必要な情報 を整理すると、その3つに分類される」からだと思います。

ですので、 AAAパターンが悪いというわけではありませんし、それが大鉄則であることは変わりません。

「どういう前提条件(準備が必要)」で、「何を実行」したら、「どうなるのか」を確認してその動作を担保する

という目的が大事になります。

Parameterized

テストの目的を重視した時にどうした方が良いのか、整理してみます。

  1. 前提条件 が分かりやすい(テスト実施内容 の差分も含む)
  2. 想定結果 が分かりやすい(テスト実施結果 の差分も含む)
  3. モック や スタブ を利用している場合、想定したものが渡されているのか を検証したい(verify)
  4. テスト工数(コスト) を減らしたい

では、上記の観点を踏まえて、具体的な方法を考えてみます。

具体的な方法

  1. テストのパターン(「前提条件」と「想定結果」)を書き出す
  2. 「想定結果」が同じものをグループ化する
  3. グループ化した中で「検証内容」が異なるものは別のグループにする
  4. グループ化した中で「前提条件」が明確に異なるものは別のグループにする
  5. テストコードを作っていく

最初からテストコードを作って良い状態であれば、「1」の状態から、コメント形式。もしくは、テスト名として先にコーディングをするのも良いと思います。複雑な条件分岐やパターンがある場合は、マトリクス表を作るのも有効です。
まずは、 テストの実施忘れがないように 集中して考えます。

その後に重要なのが、「テスト工数を減らす」ためのグループ化です。
言語にも依りますが、 コーディングの際に Parameterized(複数パターンチェック) を使い、コピペによる同じコードの量産を避けます。
(コピペコードをすると、作る時は楽ですが、検証が足りていなかった…となった時など、改修する時に地獄を見ます)

グループ化すると、多くのパターンが1つの Parameterized で良いのでは?となるかもしれません。
そう思った時には一度、「 差分含めて分かりやすい ようにするには?」を考え、別の内容であれば分割することをお勧めします。

補足

「想定結果」で分類すると記載しましたが、テスト内容によっては「検証内容」を元に分類した方が良い場合もあると思います。
言いたかった部分は 同じものは極力まとめる 方が良いということです。

ただ、まとめるのはテストコードではありません。テストパターンのみです。

業務処理では共通処理を 1つにまとめて呼び出す… といったことをしますが、テストコードでは極力避けておいた方が良いです。
「before , after で処理をしすぎるな」「AAAパターンが良い」と同じ理由です。
例外は、
意味のある最小限単位で同じ 検証処理」や「ライブラリなどに依存した、 本質的に分かり辛い準備処理 」といった、分かりやすくするための共通処理化です(工数削減に繋がるので良いと思います)。

注意したいこと①

「想定結果 が分かりやすい」と書きましたが、 やらない方が良い内容 があります。
それは、「業務処理内の変数を参照する」ことです。

理由は単純です。「変数の内容がテストコードを見て分からない」点と「変数の内容が間違っていた場合にテストの意味が薄れている」点です。
そういった部分は、 例えコピペだとしても、、 文字列であったり、数値型といった 分かりやすいものを使う ことを心掛けた方が良いと考えています。

注意したいこと②

テストコード内で条件分岐(if) が出てきた場合は、 テストの分割を検討した方が良い兆し です。

システム日時(時差含む) 値を利用したテストの場合はどうすることもできない…ということはありますが、
基本的には「条件分岐が発生する = 想定結果 や 前提条件が違う」ことが多く、それをひとまとめにすると パッと見て分かり辛くなってしまう可能性 があるため、別にしましょう。

まとめ

  • テストをする目的を明確にする
  • テストコードは、できる限りまとめる
    • そのためには、下記の順序でテスト内容を決める
      1. テストのパターン(「前提条件」と「想定結果」)を書き出す
      2. 「想定結果」が同じもの or 「検証内容」が同じもの をグループ化する
      3. グループ化した中で「検証内容」が異なるものは別のグループにする
      4. グループ化した中で「前提条件」が明確に異なるものは別のグループにする
    • グループ化/テストコード 作業にあたり下記を注意する
      1. テストの目的が満たせていること
        • 検証したい内容がテストコード内で実施されている
        • 条件によって発生し得るパターンが一度はテストされている
      2. 何をテストしたいのか(前提条件/想定結果)が分かりやすいこと
      3. 業務処理 の変数(定数) を検証結果として利用しないこと
      4. テストコードの中で 分岐処理が発生しないようにグループ化すること

最後に

人間が手作業でやらないといけないテストを時間の制約なしに、(実行にかかる時間は置いておいて…)何回でもやってくれるものが、テストコードです。
業務処理を作るのも人間で、テストコードを作るのも人間なので、間違う可能性もあります。が、それは手作業のテストでも同じです。

どうしてもテストコードで実施できないテストも勿論あります。
ですが、時間がかかる大半のテストはテストコードによって確認することができます(実装忘れがなければ…)。テストコードで証跡を集め、手作業で確認をするという方法で、少しは楽ができる部分もあるでしょう。

動作確認であれば、テスト工数は減らせるかもしれません。それほど、テストコードは工数がかかります。
ですが、継続して運用していくものであれば、その工数は初期だけです。改善をした場合にはテストコードの修正が発生しますが、デグレ確認としても機能するため、継続していくほどに初期にかかった分の費用を回収することができます。

そのためには、意義のあるテストコードを作成していきたいですね。読んでいただけた方の気付きになれれば幸いです。

他にも、「こういう工夫をしています!」といった良い方法がありましたら教えていただけますと嬉しいです。

  1. Arrange(準備) , Act(実行) , Assert(検証) の頭文字を取って、AAAパターンと呼ばれます。
    「どこまでが準備されている箇所か」「どこまでが実行箇所か」「どこまでが検証箇所か」分かりやすいようにしましょう。
    という方法のことです。

  2. レビュー指摘 や 処理の改修が入った時、同じような箇所を何度もコピペで修正していくことです。修正忘れが発生すると、気付けない不具合に繋がり、結果大きな問題になりかねません。

155
176
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
155
176