テスト駆動開発を10年以上やってきた実体験を踏まえて導入推奨する記事

・この記事を読む前に事前に予習できる方は、下記を全部見ておくと、理解が深まると思います。

* [和田卓人氏が教える、自動テストの使い方　学びを自動テストとして書く「学習用テスト」という考え方（全４話）](https://logmi.jp/tech/articles/329178)
* [サバンナ便り ～ソフトウェア開発の荒野を生き抜く～(全8回）（別記事へのリンクが無いので、URLを変更して移動してください)](https://gihyo.jp/dev/serial/01/savanna-letter/0001)
* [TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング](https://www.youtube.com/watch?v=Q-FJ3XmFlT8)
* [シンプルさの必要性](https://eed3si9n.com/ja/simplicity-matters/)
* [本：『キーエンス解剖　最強企業のメカニズム』](https://bookplus.nikkei.com/atcl/column/032900009/120800240/)

テスト全般については、テスト駆動開発の第一人者であるt-wadaさんが詳しく正統派だと思うので、そちらはt-wadaさんの記事を見てほしいです。

キーエンスの本は、エンジニアも実装だけでなく、会社の利益に繋がるかどうか？に関心を持って動く必要のある時代になったと思うので、ぜひ読んでみてほしいです。

## 最初に会社の関心ごとはなにか？

* 個人：きゅうりょうをたくさんください（異論はあると思います）
* 会社：利益を上げて賃上げして、会社も大きくする（お上が総取りする仕組みじゃないといいですね）
* 働くみんな：利益に繋がる行動かどうか考えて、業務を取捨選択し遂行する
  * この辺りの話については、キーエンスの本を参考にすると良いと思います。
  * （平均年収2000万。30代で家が建ち、40代で墓が立つと言われてます。）
  * （平均残業時間56時間くらいと書いてあったので、そこまで激務でも無さそうです）

### 結論
会社は利益を上げることが大事。
利益が上がれば、全員の給料を上げられてハッピーになれる！！

## 自動テストは会社の利益につながるのか？

* 開発速度が向上してQA人員や開発人員の人件費を削減できるかも？（コスト低減）
* ライブラリの定期アップグレードを安心してできるかも？（脆弱性対応できる）
* 何もしなくてもシステムは壊れる時代となっている
  * コロナの接触確認アプリ「COCOA（ココア）」の問題とかありましたね
  * 日本人は、やったらやりっぱなしで効果の定期測定しないのも問題かと
* なにか大きな問題があると利益がふっとぶ可能性がある（防げるかも）
* そもそもシステムがまともに動かないと利益にならない

### 結論
直接的に利益には繋がらないけど、間接的に利益に繋がると思う。

コスト削減や利益の下げに対する防波堤になるんじゃないか？
（テストがあればバグが出ないというわけではないので、そこは要注意！！）

## 具体的なテスト駆動開発のやり方

### テスト駆動開発のやり方

この記事上部にあるt-wadaさんのライブコーディングを参照してください。

### テストの書き方の話

下記の記事が分かりやすいです。
https://gihyo.jp/dev/serial/01/savanna-letter/0007
`describe('適格簡易請求書の消費税額計算'` の最終系のコードのところ。

真のリーダブルコードは、コメントが必要だということがよくわかります。

コードには何をするか？ということが書いてあるが、なぜこのコードになったのか？までは表現できないので、背景に関しての説明はコメントを書くしか無いのだと思います。

### おまけ

下記に、t-wadaさんの過去記事をまとめているので興味があれば読んでみてほしいです。
https://qiita.com/daijinload/items/3a884037875f317f813f

テストだけでなく、設計の話などの深い話もされていて大変勉強になります。

## ここからは僕の実装スタイルについて

ざっくりいうと、かなり雑にテストを書いています。
（雑ではありますが、正常系と異常系のテストを両方書いてます。）

動作の確認をテストで行って、最後にブラウザでサラッと確認するスタイルです。

## 基本テスト方針

基本は、`Worse Is Better` と　シンプルを目指します。

`Worse Is Better` の t-wadaさんの解釈は下記となっています。

> 「設計の正しさ/美しさと実装の単純さが対立する（両立できない）ときは、実装の単純さを選択する。
> そうした方が、たとえそれが漏れのある抽象になったとしても現実の問題を解決し、
> 実装の単純さによって開発参加のハードルが下がり、進化的な強さを獲得できる」というもの

さすがの解釈だなと思います。

また経験上、理解が難しいコードは、可読性が低いコードとなり、ダメな実装だと思います。

初心者でも容易に理解できるコードを目指すべきだと思います。

## 用語解説（この後の説明で必要なので）

* BDD(Behavior-Driven Development)
  * 振る舞い駆動開発
  * API単位でテストを書くイメージ（ざっくり言い過ぎで異論はあるかもですが。。）
* smallテスト
  * unitテストの領域
  * プロセス内で完結するテスト。DBは他プロセスなのでアクセスNGなど
* mediumテスト
  * integrationテストの領域
  * local PC内で完結するテスト。他サーバのAPIなどはPC外なのでアクセスNGなど
* largeテスト（無制限）

## 具体的に注意していること

* 基本は、BDDスタイルのAPI単位でテストを記述する
  * 数が増えてくると実行速度が遅くなるのが難点
  * 対策としては下記となりそう
    * smallテストを増やしていく
    * 言語自体の速度が速いものにする
    * 並列でテストを処理する（並列だとテストカバレッジは取れない。。。）
* ローカルでできることは全てやる（モックは外部APIなど最小限にする）（mediumテスト）
  * モックを最小限にするとDIがほぼ必要なくなるので、僕が設計するときはDIは、ほとんど使わないです
* テストコードを仕様書としても見れるように記述する
* BDDかつmedium領域のテストだと、実行順序をランダム化するのは難しいため、しない
* API単位でのテストは実行速度が遅いので可能な限りsmallテスト側に寄せる
  * リクエストデータのチェック処理などは、関数単位でテストできる実装が良いと思う
    * そうすれば、API単位のmidiumなテストの一部をsmallテストで実装できる
* smallテストは、テストの実行順序をランダム化しやすいので意識してもよい（でも強制はしない）
* 画面から実行して確認しているものを、テスト側で確認できるようにする
* パラメータパターンが多いメソッドのテストは、パラメタライズドテストを使用して書く
* データの作成はInsert文を書かないようにする。既存ロジックを動かしてデータ作成する
  * Insert文の管理やメンテが不要になる
  * ロジックを変更したときに、ロジックでデータ作成していれば、不具合があればすぐに気づける
* 全データを消すテストを書かない（間違って本番DBにテストを実行しちゃったとしても安心感を得るため）
* テストが安定して動くように注意すること（たまにエラーになるテストは書かないこと）
* 日付のテストをしやすくするために、引数に日付を渡すように実装する（メソッド内で `Date.now()` しない）
  * 画面描画のテストでも描画処理内でAPIを読んでデータを取得せず、引数でデータ受け取るようにする等
* テストコードの上の方に、そのテストを単品で実行するためのコマンドをコメントで書いておく
* コードカバレッジを出して、自分の予想する動作範囲と実際のコードの動作範囲が合っているか？確認すること
  * 全部通すテストを書いたはずなのに通ってない部分があるとか、通らないはずの場所が通っているとか無いようにすること

実際のサンプルなど、あればリンクを追記したいと思います。

## 経験談（新規でテストを入れたパターン）

* ゼロベースでシステムを作ったのは、PHPで小さいのが1個。Golangで1個とTypeScriptで1個の合計３システム。
  * API単位だけでテストを書いているのに、カバレッジが93%ぐらいあった
  * （一般的には80%以上だと良いと言われている。）
  * （100%を目指すべきではないとも言われています。）
  * 大規模リファクタリングを３回くらいやっても、バグは出なかった（テストがコケてくれて気づけた）
* 新規だとテストが完備できるので、後から入った人はテストを書いてくれる人が多かった。

少数だけど、結局書かない人もいた。
（結局、なんやかんや言って、理想を目指すのではなく自分ファーストなだけなんだなと思った。）

## 経験談（既存でテストが無い状態でテストを入れたパターン）

* 大抵の現場はテストの残骸があった（途中からやらなくなったっぽい形跡があった）
* ログインとかはなんとかしてくれているので、APIテストが書けることが多かった
* まずは、自分の実装範囲でテストを導入していって、コードを変更する部分があれば随時テストを追加していくスタイルでいった
* テストを書きだすエンジニアもいたが、それは少数で大多数は今まで通りテストを書かないエンジニアが多かった
  * （テストを書いてみて良さを実感しないと、テスト導入は難しいという印象。）
  * （あと、若い人は大抵書いてくれた。これだから最近の若いもんは良いですね＾＾）
* また、酷い人はコケたテストをコメントアウトしてコミットしていた。。。
  * （その後、僕が動くように変更した。。。）
* QAチームがいる現場ではエンジニアが動作確認もせずにQAさんにテスト依頼して動かしたらエラーとかあった
  * 上記は良く無いが、かといって、QAエンジニアばりにテストするのも違う気がする
  * 自動テストを書くまでをプログラマの仕事と線引きすると、良い塩梅なのでは？と思う

後から参加したシステム開発でテストが無い状態でテスト導入はかなりキツイです。

長く続くシステムで、テストを書かないという行為は、鬼畜の所業ではないか？と割りとガチで思います。

開発が止まったメンテ状態のシステムでもライブラリのアップグレードなど必要となるので、そういうときにテストがあるのと無いのとでは大違いです。

自分が抜けた後のことも考えて仕事してくれると助かります。
（これは何かあった時に呼びつけられる系エンジニアとしての願いでもあります）
 
## フロントもテストできる

* 紙芝居モードが実装されている（サーバと通信せずに画面を紙芝居のように動作させる機能があると良い）
* ヘッドレスブラウザを動かして全画面のキャプチャを取れる
  * ブランチを指定してのキャプチャ画像の比較が簡単にできる
* 画面固有のロジックがあれば、ユニットテストも用意する
  * フロントに業務ロジックは置かないほうが良いので置かないといらないのでは？
    * 過去プロジェクトでは、文字コード判定などユニットテスト出来るところはしていた

経験談としては、フロントエンドはテストしづらいという免罪符を掲げて、テストを書かないエンジニアが多い印象。

今の時代そんなことはないので、テスト書きましょう！！

特に画面のスクショDiffは、僕みたいなデザインのズレを認識できないエンジニアにも変化を教えてくれて最高のテストだと思います。

## Playwrightというテストツールが良さげ

https://zenn.dev/cybozu_frontend/articles/6c2a54196f056d

かなり色々できるので導入すると捗りそうです。

Playwrightに限らず自動化ツールも進化していくので、そのときに良さそうなものを導入していくと良いと思います。

昔から比べるとAIなども使われていて、今の時代は随分便利になったものです。

## テスト駆動開発の良いところまとめ

* テスト駆動開発による確認作業の時短
* バグが減ることによる開発時間の増加
* リファクタリングを安心してできるのでコード品質が向上
* デグレードへの耐性付与
* テストが動く仕様書となってメンテされ続ける
* システムの安定した引き継ぎができる
* QAチームの負荷軽減
* 転職時にテスト書いてましたと言える
  * （テスト書いてない現場はそれだけで技術力低そうに見えませんか？）

## テスト駆動開発導入のその先に

手動テストを無くすというのを考えてみると良いと思います。

完全に自動化できれば属人性も排除できますし、日に数十回のリリースも可能となります。
（日本の少子化トレンドの中で、QAテストに掛かる人員配備やコストを無くすことができます。）

下記の記事を見ると、1000社中30社は完全置き換えに成功しているようです。

[【第2回】世界に広がる「ソフト・アプリ自動テスト」の波。MagicPodが提供する“日本らしい”自動化ツールの魅力とは？／～ソフトウェアテスト自動化の今とこれから～](https://u-note.me/note/71024)

> 40カ国のソフトウェア会社1000社（2022年6月時点、従業員数30名以上）に対するアンケート
> 自動テストによって手動テストの作業を100%置き換えることに成功した企業は3%でした。

医療系などの障害発生がクリティカルなものでは完全自動化は難しいと思いますが、それ以外では、案外可能なのでは？と思います。
（僕自身、完全自動化は見たことが無いですが、やってみたいところです）

## 最後に

テストを書いたことが無いと、正直良さがわかないと思います。

なので、騙されたと思って、とにかく書いてください。

雑でも低レベルでも構わないからテストを書いてください。
（書けば上達していきます。）

テストは最悪捨ててしまってもシステムは稼働するので、気楽に書いていってください。
（もちろん捨てないでメンテしていってほしいですが。。。）
（あと、現場に残されたテストの残骸は、捨てられたそれだったのかもしれません。。。）