はじめてのテスト
テスト
テストに関する重要な概念とかをメモしたい
DI
依存性注入
そもそもテスト系から生まれた言葉なのかどうか不明だが、オブジェクトや関数同士が依存していると当然テストしずらいから、依存関係は引数とかで外から入れましょう、っていうこと?そうすれば別々にテストしやすいし、モックとかも使いやすいやん、という
もともとはSOLID原則関連の話らしい
DIは制御の反転の一種で、オブジェクトの作成と利用について関心の分離を行い、疎結合なプログラムを実現することを目的としている。
DIをするとテスタブルになるものの、インスタンスの生成が面倒になる(new ObjectA(new ObjectB, new ObjectC)
みたいになる)。それを解決するためにDIフレームワークというものがあるらしい(バックエンドの話?)
Property Based Testing
Property Based Testing は、Haskell のライブラリ QuickCheck からはじまったテストスタイルで、定義された条件に合わせて自動生成された膨大な値に対してテストを行う手法です。
ランダムに値を生成しまくって、テストしまくる手法。
fast-checkというjsライブラリでいうと、標準で100回テスト、pre/filter/mapを使い生成値を調整してテストする。
対して、開発者が特定のシナリオやケースを考え、それに対する期待結果を定義する僕らが普通にイメージするテストはExample Based Testing。
たぶん、こういう時はバグる可能性があるかもなという具体的な値(Example)ベースでテストを考えるか、このPropertyはXXという条件のある整数値であるべきだ、みたいなあるべき姿を定義してランダムにテストするか、的な
パターン、ベストプラクティス、概念
arrange-act-assertパターン
// Arrange(準備)
var guid = new Guid("01234567-89ab-cdef-0123-456789abcdef");
// Act(実行)
var actual = guid.ToString("D");
// Assert(検証)
Assert.AreEqual("01234567-89ab-cdef-0123-456789abcdef", actual);
Mutation Testing
偽陽性、偽陰性の検出手法
偽陰性の検出方法の一つとして、「エラーシーディング」と呼ばれる意図的に不具合を仕込んで問題点を洗い出す手法がある。さらにこの手法を体系化しプロセスまで定めたもので「Mutation Testing」というものがあると知った
Google入れてるのか…なんとなく広まらなそうだけど概念はおもしろい
テストの分類
粒度?フェーズ?による分類
- 単体テスト:モジュール単位
- 結合テスト:複数モジュール
- システムテスト:システム全体
- 受け入れテスト:顧客の受け入れテスト
実行方法?による分類
- 動的:実行してテスト。jest/playwrightとか
- 静的:実行せずテスト。静的コード解析とか。Linterとかtypescriptとか
技法?による分類
- ホワイトボックス:内部構造を理解してテスト
- ブラックボックス:内部構造分からずテスト
- グレーボックス:中間
テストの目的?による分類
- 機能テスト:ソフトウェアが仕様どおりに動作するかどうかを検証
- 非機能テスト:ソフトウェアの性能、安全性、セキュリティなどの品質を検証
テスト手法?
- スナップショットテスト
- リグレッションテスト
- ビジュアルリグレッションテスト
マーケティング文脈?
スナップショットテスト
DOM構造を変更前後で比較するテスト?画面スクショを撮る(→VRT)わけではない?
ビジュアルリグレッションテスト(VRT)
こっちは画像をとって比較する。スナップショットテストより正確だけど重い
そもそもリグレッションテスト
スモークテスト
storybookでスナップショットテストのスナップショットファイルを見てたらsmoke-test
と書いてあった
コンパイルやビルドのエラーは出なくなった後、できあがったソフトウェアが機能テストなどの本格的なテストを実施可能か判断するための予備的なテストという位置づけになります。
スモークテストという名称の語源は、電気工作の分野の通電テストにおいて、基板に電流を流して発煙の有無を確認したことに由来すると言われています。
サニティテストというのも出てきた。
みんな好き勝手テストを増やしてないか?笑
うーん、なんかいまいち具体性がないが、最低限動くのかどうかみたいなテスト?
テストをSMLで分類
ライブラリ、フレームワークとその概要
- Jest
- Testing Library
- storybook
- Chai
- Cypressで使われてた
- Mocha
- Cypressで使われてた
- Playwright
- Cypress
TDD(Test Driven Development: テスト駆動開発)
- メリット
- 早期バグ検知
- テストカバレッジが上がりやすい
- 仕様を明確にしやすい
- テスタブルな実装に自然となる
- 修正の心理的負担軽減
- デメリット
- 慣れてない、やったことない、社内に知見がない → 学習コスト
- 自動テストを書くコスト
- テストのメンテナンスコスト
用語
- 三点(三角)測量
- 新しい機能やアルゴリズムを実装する際に、複数の異なるテストケースによってその実装を確定させていく方法
- そのテストがたまたま通過したということが起こらないように、状況によって、2つ以上の値を使用しテストを書く。
- レッド・グリーン・リファクタリング
- レッド (Red)
- 最初に、まだ存在しない機能や修正に対するテストを書く。このテストは最初は失敗する (レッド状態) ことが期待される。
- グリーン (Green)
- 書かれたテストをパスさせるために必要な最小限のコードを書く。テストが通る (グリーン状態) ことが保証される。
- リファクタリング (Refactor)
- テストがパスした後、コードの品質を高めるためのリファクタリングを行う。重複の排除やコードの整理を行い、機能は変えずに保守性を向上。
- テストを通すために発生した重複を全て除去
- レッド (Red)
- 仮実装
- コードでまずベタ書きの値を使い、実装を進めつつ、徐々に変数に置き換える。
- どんな手段であってもとにかくテストを成功させる
- 明確(明白)な実装
- 「こう実装すれば絶対にうまくいく」と浮かんでいる段階でわざわざ仮実装しなくても、その実装を落とし込めばいい
参考
- gpt
- https://qiita.com/takagi-shun/items/a7465ccaffbf6108e1b6
- https://dev.classmethod.jp/articles/what-tdd/
きれいにする → 動く、ではなく、動く → きれいにする、を目指すもの?
- きれいにする → 動く:せっかくきれいなのに崩したくない
- 動く → きれいにする:動くんだからいいじゃん
どちらのアプローチも、「きれいで動く」状態に行くには壁がある。TDD は2における壁を低くする手法?
微妙に用語揺れが起きてるのはなんなんだろ。
BDD との違い
TDD(テスト駆動開発)と BDD(ビヘイビア駆動開発)は、両者ともにテストを先行して書くことを重視
TDD(Test-Driven Development)
提唱者 :Kent Beck
基本概念 :
- Red:まず、失敗するテストケースを作成します。これはまだ実装されていない機能のため、必ず失敗します。
- Green:テストを通過させるために、最小限のコードを実装します。
- Refactor:コードを清書し、設計を改善します。このとき、テストが再度失敗しないようにします。
目的 :
- コードの品質向上
- 迅速なフィードバックループの確立
- リグレッションテストの機能
BDD(Behavior-Driven Development)
提唱者 :Dan North
基本概念 :
- Scenario Writing:自然言語(例:Gherkin 形式)で仕様を書くことにより、仕様や振る舞いを理解しやすくします。例えば、「Given-When-Then」というフォーマットで書かれます。
- Given:前提条件
- When:実行するアクション
- Then:期待する結果
- Shared Understanding:開発者、テスター、ビジネス担当者が共通の理解を持つようにし、コミュニケーションのギャップを埋めます。
- Automation:シナリオに基づいて、自動化されたテストを実装します。
目的 :
- 共通の言語を用いたコミュニケーションの改善
- 開発者と非開発者の間のギャップを埋める
- 仕様がそのままテストケースになることにより、要求から実装への一貫性を保つ
なぜ BDD を提案したのか?
問題意識
- コミュニケーションの障害
- TDD では主に技術者間での議論に焦点が置かれがちであり、ビジネス担当者とのコミュニケーションが疎かになりやすい。
- BDD では、「Given-When-Then」形式を使うことで、ビジネス担当者が理解しやすい形式で仕様を表現し、チーム全体の共通理解を深めることが可能になります。
- 要件理解の不足
- TDD はテストコードの分類が詳細なため、大局的なビジネス要件の理解が疎かになることがあります。
- BDD はシナリオベースでの開発を行うため、まずビジネスルールや要件の視点を明確にし、それをテストケースとして具体化することができます。
- テストケースのドキュメント化
- TDD の場合、テストコードは技術者向けの記述が多く、非技術者には理解しづらいケースがあります。
- BDD は自然言語で記述されたシナリオがそのままドキュメントとして機能するため、非技術者でも読みやすく、要件の追跡性が高まります。
結論
TDD と BDD は共に高品質のソフトウェア開発を目指すが、TDD はコードの単体テストに重点を置き、BDD は振る舞いに基づく仕様のコミュニケーションと理解の共有を重視。BDD は特に、ビジネス担当者を含む全チームメンバーとのコミュニケーションの効率化を目的としている。
現在はTDDについてはこちらを参照するのが一番ですよ!
おお、なるほど、ありがとうございます!
テスト対象、範囲
結果の80%は、全体の20%の要素によって生み出されている byパレートの法則
雑な話だが、テスト工数たいして取れないけど自動化はしたいみたいなときにパレートの法則を使えんだろうか。全体の20%のカバレッジを一つの目安にする。
- C0:命令網羅
- 全命令文を経由
- C1:分岐網羅
- 全条件文(IF節単位)パターンを網羅
- C1カバレッジが100%の場合、必然的にC0カバレッジも100%
- このくらいが一般的に妥当。C2だとちょっとやり過ぎ?
- 型的に入れられない値も必要だったりする→これが理由なら100じゃなくてもOK
- 例)BFFがundefined許容してるけど、FE許容してない、とか
- C2:条件網羅
- 全条件(条件単位)パターンを網羅
- C2カバレッジが100%であっても、C0カバレッジ、C1カバレッジが100%になるとは限らない
- MCC:複合条件網羅
- 全条件パターン網羅
- ケース数が一気に増える
- Lines
- C0?
- Statements
- C0
- Functions
- Branches
- C1?
- なんかC2っていうサイトもあった
とのマッピング
よくないテスト
原因
- 実行順序に依存
- 実行時まで値が決定しない再現性のないテスト
3. Dateとか。これはPlaywrightのclock APIとか環境は揃いつつある気がする - フレームワーク理解不足
- CIなどテスト実行マシンリソース不足
良いテストコード
単体テストの考え方めちゃでてくるなやっぱ…積読したままになってる。
単体テストの目的=ソフトウェア開発PJの成長を持続可能なものにする
- 退行に対する保護
- リファクタ耐性
- 迅速なフィードバック
- 保守しやすさ