見出し画像

TotT: 詳細クラスのテストより、公開APIのテストを優先しよう

くぼぴー / note inc.

こんにちは、kubopです。
Googleにはトイレテスト(TotT)という文化があるようで、テストに関するTipsをトイレに貼り出し、テストに関する知識を全社で共有しているらしいです。
昨今はリモート勤務が広まり、TotTの実施は難しく、SlackやBotを用いてもなかなか浸透するかどうか…
そこで、noteに書きつつ自分が勉強するために、少しずつ読んで内容や、所感を書いてみようと思います。
※ 翻訳・解釈の間違いなどあるかもしれません。
その場合はこっそり教えてください。

もっとテストを書いてほしいのです。そう、あなたです。テストは、コードをリファクタリングするときや他の開発者が機能を追加するときに、 あなたを守るセーフティネットであることはもうおわかりでしょう。また、テストがコードの設計に役立つこともご存知でしょう。

私たちは、この秘密兵器(this secret weapon)を世界中の人々と共有し、私たちのテストへの情熱を広めるとともに、あなた自身やあなたの会社の他の人たちに、この重要なトリックやテクニックを楽しく簡単に学んでもらうことにしました。
このブログで定期的にエピソードを紹介し、PDFを提供しますので、プリントアウトしてご自分のバスルーム、廊下、キッチン、月面基地、秘密の地下要塞、億万長者の創業者のプリウスなど、どこにでも貼り付けてください。

Introducing "Testing on the Toilet"

Testing on the Toilet: Prefer Testing Public APIs Over Implementation-Detail Classes

以下のクラスには、テストが必要だと思いますか?

class UserInfoValidator {
  public void validate(UserInfo info) {
    if (info.getDateOfBirth().isInFuture()) { throw new ValidationException()); }
  }
}

UserInfoValidatorには分岐などのロジックがあるため、テストがあることは良いかもしれません。
しかし、UserInfoValidatorを利用するクラスが、唯一UserInfoServiceだけだったとしたらどうでしょう?

public class UserInfoService {
  private UserInfoValidator validator;
  public void save(UserInfo info) {
    validator.validate(info); // Throw an exception if the value is invalid.
    writeToDatabase(info);   
  }
}

答えは、「UserInfoServiceを通してすべてのパスをテストすることができるので、おそらくテストは必要ない」です。
重要な違いは、このクラスは実装の詳細であり、公開APIではないということです。

--

パブリックなAPIはどこからでも複数から呼び出しがあり、ユーザーはそのメソッドに可能な限りの組み合わせの入力を渡すことができます。
そのため、APIを利用する際に問題が発生しないように十分にテストされていることが必要です。

※パブリックAPIの例としては、コードベースの異なる部分で使用されるクラス(例えば、クライアントサイドで使用されるサーバーサイドのクラス)や、コードベース全体で使用される共通のユーティリティクラスがあります。-> ControllerやServiceなど。

実装詳細クラスは、公開APIをサポートするためだけに存在し、依存関係が限られています。
これらのクラスは、それらを使用する公開 API をテストすることで、間接的にテストすることができます。

実装詳細クラスのテストは、クラスが複雑な場合公開 API クラスのテストを書くのが大変な場合などの場合は有効です。
実装詳細クラスをテストする場合は、そのメソッドに渡されることのない入力があるため、パブリックAPIほど深くテストする必要はありません。
(上記のコードサンプルでは、UserInfoValidator.validateへの引数としてNULLが渡されると何が起こるかをテストしても意味が無い。)

実装詳細クラスは、別のクラスにたまたま存在するプライベートメソッドと考えることができます。実装詳細クラスは、可視性を制限するようにしましょう。

実装の詳細なクラスを頻繁にテストすると、いくつかの問題が発生します。

  • 実装詳細クラスのリファクタ時にテストもリファクタする必要があるため、コードの保守が困難になる。

  • 実装詳細クラスだけテストを行う場合、APIの動作は保証されず、複数の呼び出しパターンに対応できません。

🤔

Rubyで書くとこんな感じかなぁと予想。

class UserInfoValidator
  def validate(user_info)
    raise ValidationException if info.getDateOfBirth().isInFuture()
  end
end
class UserInfoService
  def self.save(user_info)
    UserInfoValidator.new().validate(user_info)
    user_info.save
  end
end

UserInfoValidator.validate()はprivateメソッドと同じ扱いになるので、テストは不必要な感じなのかな。
rubyだとpackage-privateは難しそうだから、利用が複数に散らばらないように考える必要があるかも。

Licensed under a Creative Commons
Attribution–ShareAlike 4.0 License

この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
くぼぴー / note inc.
note/サーバサイドエンジニアです。 ここ最近はQA活動をしております。 哲学とかそういうのが好きです。 何か新しい考えが浮かんだら記事を書いていきたいです。