mcpField
T
test-anti-patterns

test-anti-patterns

github.com/dotnetテスト・品質5.8k4.6k 回インストール1 日前に更新
リポジトリ

test-anti-patterns について

test-anti-patternsは、テスト品質を見直す開発者向けのスキルです。信頼性、保守性、診断性を損なう問題を複数の言語やフレームワークで調べ、重要度別に整理します。

主な機能

  1. 01テストのアンチパターンと品質問題の検出
  2. 02重要度別の指摘整理
  3. 03複数の言語とフレームワークへの対応
  4. 04指定した懸念点に絞ったレビュー

活用シーン

  1. 01テストコードのテストスメル確認
  2. 02不安定または信頼性の低いテストの調査
  3. 03診断結果を得るためのテスト監査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dotnet/skills --skill test-anti-patterns

インストール後、Claude Code で/test-anti-patternsを入力して読み込まれたか確認します。

詳細

テストのアンチパターン検出

サポート対象言語のテストコードを素早く実用的に分析し、テストの信頼性、保守性、診断価値を損なうアンチパターンや品質上の問題を特定します。

言語固有のガイダンス:必要に応じて、呼び出し元から提供された、または実行時に一覧表示された test-analysis-extensions カタログと、対応する言語ファイルを参照してください。 参照専用の ヘルパーは呼び出さないでください。ファイルが存在しない場合は、以下の組み込みフレームワークルールを使用してください。

使用する場面

  • ユーザーがテスト品質のレビューや、テストスルの発見を求めている
  • ユーザーがテストが不安定または信頼できない理由を知りたがっている
  • ユーザーが「テストは適切ですか?」または「テストの何が問題ですか?」と尋ねている
  • ユーザーがテスト監査やテストコードレビューを依頼している
  • ユーザーが改善内容を決める前に、診断結果を求めている

使用しない場面

  • ユーザーが新しいテストをゼロから作成したい場合(code-testing を使用)
  • ユーザーが診断レビューではなく、直接的な実装修正を求めている場合(該当する書き込み/編集スキルを使用)
  • ユーザーが MSTest の Assert.AreEqual の引数順序の入れ替えを修正したい場合(writing-mstest-tests を使用)
  • ユーザーが MSTest の DynamicData を IEnumerable<object[]> から ValueTuple に変換したい場合(writing-mstest-tests を使用)
  • ユーザーがテストを実行したい場合(.NET では run-tests を使用)
  • ユーザーがテストフレームワークやバージョン間の移行をしたい場合(移行スキルを使用)
  • ユーザーが .NET のカバレッジを生のまま収集したい場合(run-tests を使用)、.NET 以外のカバレッジを収集または分析したい場合(ネイティブツールを使用)、プロジェクト全体の .NET カバレッジ/CRAP メトリクスを求めている場合(coverage-analysis を使用)、または名前付きターゲットの .NET CRAP を求めている場合(crap-score を使用)
  • ユーザーがテストでバグを検出できるかを尋ねている、または振る舞い/擬似ミューテーションの不足を知りたがっている場合(test-gap-analysis を使用)
  • ユーザーがテスト構成や正常系とエラー系の分類、標準化されたタグ付け、または特性/カテゴリの分布を求めている場合(test-tagging を使用)
  • ユーザーが学術的な分類体系と拡張カタログを用いた、厳密で詳細なテストスル監査を求めている場合(test-smell-detection を使用)

入力

入力必須説明
テスト対象範囲いいえ分析対象のテストファイル、クラス、ディレクトリ、またはプロジェクトです。省略された場合は、現在のワークスペースから検出します。
本番コードいいえテスト対象のコードです。テストで検証すべき内容を理解するために使用します。
個別の懸念事項いいえレビュー範囲を絞り込むための「不安定さ」や「命名」などの焦点です。

ワークフロー

ステップ 1:言語を検出して拡張機能を読み込む

入力を求める前に、現在のワークスペース内で指定されたテストパスを解決してください。 パスが指定されていない場合は、リポジトリのマニフェストと一般的なテストマーカーを使用して、 現在のディレクトリ以下からテストファイルを検出してください。このスキルのコンテキストにある Base directory はドキュメントの保存場所であり、ユーザーのワークスペースではありません。 対象ファイルをそこからの相対パスで解決してはいけません。

あるリーダーでパスが見つからなくても、ワークスペースの glob/検索で見つかった場合は、 その正確なパスを正規化して再試行してください。シェルのテキストリーダー(Unix では sed/cat、PowerShell では Get-Content)を使用できるのは、リーダーの可用性、 転送、またはパス正規化に関する失敗が確認された場合に限ります。また、その場合も 正規化されたパスが引き続き現在のワークスペース内にあることを確認してから使用してください。 コンテンツの除外、権限/ポリシー、ワークスペース境界、または原因不明の失敗が発生した場合は中止してください。 許可されたリーダーでアクセスできる検出済みファイルは、すべて監査してください。 ユーザーにファイルの貼り付けを求めてはいけません。許可されたリーダーがすべて失敗した場合は、 セキュリティ境界を迂回せず、正確な阻害要因を報告してください。

言語とフレームワークを特定してください。必要に応じて、対応する同梱のリファレンスを読み込み、 ファイル名は提供されたカタログを基準に解決してください。 既知のリファレンスディレクトリだけを確認し、インストールディレクトリは確認しないでください。 リファレンスを利用できない場合は、以下のカタログを使用し、リファレンスが利用できないことを報告してください。

ステップ 2:テストコードを収集する

本文を読む前に、対象範囲を確定します。1 つのファイルまたはクラスが対象の場合は、その範囲を直接読みます。プロジェクトまたはテストスイートが対象の場合は、テストファイルを一度だけ特定し、ツールが対応していれば独立した読み取りをまとめて行い、特定したすべてのテストとクラスレベルのフィクスチャに判定が付いた時点で終了します。

拡張機能の検出マーカーが読み込まれている場合はそれを使用します。そうでない場合は、このスキルに組み込まれたマーカー([TestClass]/[Fact]/[Test]`` などの属性、test_.py、.test.、_test.go、_spec.rb、#[test]、.Tests.ps1、TEST(...)、TEST_CASE(...)`)を使用します。

無関係な本番コードを全体的に読まないでください。疑わしい各テストについて、アサーション、変換、同一性の契約、または隣接するテスト漏れが実際に問題かどうかを判断するために必要な、本番コードの対応するシンボルを開いてください。ファサード/サーフェス領域にまたがる体系的なパターンの場合、呼び出されるすべてのメンバーが対象となります。小規模な本番型全体を読むか、呼び出される各メンバーを調べてから、各弱いテストが検証すべき正確な観測可能な結果、例外、状態変化、または境界を対応付けてください。

ステップ 3:アンチパターンを確認する

各テストファイルを、以下のアンチパターン一覧に照らして確認します。指摘事項は重大度ごとにまとめて報告してください。拡張機能のマッピングが読み込まれている場合はそれを使用し、そうでない場合は一覧にあるフレームワーク横断の例を使用します。

レポートを作成する前に、すべてのテストメソッドとクラスレベルのフィクスチャ/リソースについて、非公開の完全性台帳を作成します。各行には、検証手段(またはその欠如)、例外処理、状態/時間への依存、並行安全性、事前条件とアサーションの順序、および判定を記録します。すべての行が指摘事項に紐付けられるか、問題なしと明示的に判断されるまで、公開しないでください。特に次の点に注意してください。

  • actual != oldValue は弱い変更検証です。誤った新しい値をすべて受け入れてしまいます。正確な期待値を要求してください。
  • 未使用または破棄されていないクラスレベルのリソースも含めてください。メソッドだけを調べると、静的な HttpClient のようなフィールドを見落とします。
  • 同期されていない静的/グローバルコレクションをテストが読み書きする場合、順序依存かつ並列実行に安全でないものとして扱ってください。また、非 null であることを証明するためのアサーションより前に、null 許容の結果を逆参照している場合も指摘してください。
  • 本番コードが提供されている場合は、指摘事項に隣接する、明らかに未テストの契約に言及してください。ただし、網羅的な分岐分析やミューテーション分析は行わないでください。より広範な分析は test-gap-analysis に委ねてください。
重大 -- 誤った安心感を与えるテスト
アンチパターン確認する内容
アサーションがないコードを実行するものの、何もアサートしないテストメソッドです。アサーションのないテストが成功しても、何の証明にもなりません。.NET では Assert.* がない箇所、pytest では assert も pytest.raises もない関数、Jest では expect(...) がない箇所、JUnit では assert*/assertThat がない箇所、Go では t.Error*、t.Fatal*、testify のいずれも呼び出さないテスト、RSpec では expect のないブロック、Pester では Should がない箇所を確認します。モック呼び出しの検証(verify(mock)、expect(mock).toHaveBeenCalled、Should -Invoke)は実際のアサーションです。
非同期アサーションで await がない(JS/TS、.NET、Python、Kotlin、Swift)await/return のない expect(promise).resolves.toBe(x)、await されていないコルーチンを含む pytest-asyncio のテスト、Assert.ThrowsAsync を await せずに呼び出す async Task の xUnit テスト、runTest のない Kotest のサスペンドテスト、await のない Swift Testing の非同期テストです。これらのテストは、基になるアサーションが失敗する場合でも、気付かれないまま成功します。
カバレッジ稼ぎ型のすべてのパブリックメンバーを、多くの場合アルファベット順または宣言順に、機械的に呼び出すテストクラスです。意味のある結果をアサートしません。通常、各テストは var result = sut.MethodName(...)(または result = sut.method_name(...)、sut.methodName()、sut.MethodName(t))を実行しますが、アサーションがないか、単純な null/None/nil の確認しか行いません。目的は動作を検証することではなく、コードカバレッジの指標を水増しすることです。アサーションのないテストが単独で存在する場合とは異なり、実質的な検証を伴わず、対象範囲を体系的にカバーしていることがこのパターンの特徴です。
自己参照アサーションAssert.AreEqual(dto.Name, dto.Name)、Assert.AreEqual(result, result) などのように、期待値が同じ実際の値から計算されています。有効な同一性、複製、シリアル化、または往復変換の契約で、出力と入力を比較しているというだけで、このラベルを付けてはいけません。そのようなアサーションは失敗することがあります。代わりに、入力が変換処理を実行しているか、また、独立して分かっている表現、フィールド、参照の同一性、無効な入力に関するアサーションが欠けていないかを確認します。
例外の握りつぶしtry { ... } catch { }、再スローもアサーションも行わない catch (Exception)(.NET)、pass だけの裸の except: または except Exception:(Python)、try { ... } catch (e) {}(JS/TS/Java)、再パニックもアサーションもない defer recover()(Go)、アサーションのない rescue StandardError(Ruby)、テストでエラーを握りつぶす Result::unwrap_or(...)(Rust)、空の catch ブロック(Kotlin/Swift)です。
catch ブロック内のみのアサーションtry { Act(); } catch (Exception ex) { Assert.Fail(ex.Message); }(および他の言語での同等の記述)です。代わりに Assert.ThrowsException/pytest.raises/expect(fn).toThrow/assertThrows/assert.Error(t, err)/#[should_panic]/Should -Throw/EXPECT_THROW を使用してください。結果が誤っていても、例外がスローされなければテストは成功します。
常に真となるアサーションAssert.IsTrue(true)、Assert.AreEqual(x, x)、assert True、expect(true).toBe(true)、assert.True(t, true)、assert!(true)、または決して失敗しない条件です。
コメントアウトされたアサーション無効化されているのにテストは実行され、カバレッジがあるかのような印象を与えるアサーションです。
高 -- 問題を引き起こしやすいテスト
アンチパターン確認する項目
不安定さを示す兆候同期に壁時計によるスリープ/待機を使用している:Thread.Sleep / Task.Delay(.NET)、time.sleep(Python)、setTimeout / await new Promise(r => setTimeout(...))(JS/TS)、Thread.sleep(Java/Kotlin)、time.Sleep(Go)、sleep(Ruby/Bash)、std::thread::sleep(Rust)、Start-Sleep(Pester)、std::this_thread::sleep_for(C++)。抽象化せずに壁時計の時刻を読み取っている:DateTime.Now/UtcNow、datetime.now()/datetime.utcnow()、Date.now() / new Date()、System.currentTimeMillis()、time.Now()、Time.now、Instant::now()、Date()/Date.now、Get-Date、std::chrono::system_clock::now。シードを設定していない乱数:new Random()、random.random()/random.randint()、Math.random()、new Random()(Java/Kotlin)、シードなしのrand.Int()、rand(Ruby)、rand::random()(Rust)。環境に依存するパス(ハードコードされたC:\...、/tmp/...、ネットワークホスト)。
テストの実行順序への依存テスト間で変更される静的/グローバルな可変状態。状態を完全にリセットしないセットアップ([TestInitialize]、setUp、beforeEach、before(:each)、BeforeEach、t.Cleanup)。単独で実行すると失敗するが、スイート内では成功する(またはその逆の)テスト。言語ごとの例:static フィールド(.NET/Java)、モジュールレベルのグローバル変数(Python)、テストファイルのトップレベルのlet/const(JS/TS)、varパッケージグローバル変数(Go)、クラス変数(Ruby)、static mut/lazy_static!/OnceCell(Rust)、$script:変数(PowerShell)。
過剰なモック実際のテストロジックよりモックのセットアップ行数が多い。結果ではなく、モックに対する呼び出しの厳密な順序を検証している。テストが所有する型をモックしている。言語ごとの例:Moq/NSubstitute/FakeItEasy(.NET)、unittest.mock / pytest-mock(Python)、Jest の自動モック / Sinon(JS/TS)、Mockito/PowerMock(Java)、gomock/testify mock(Go)、RSpec mocks/mocha(Ruby)、mockall(Rust)、MockK(Kotlin)、Mock cmdlet(Pester)、gmock(C++)。.NET で詳細なモック監査を行うには、exp-mock-usage-analysisを使用してください。
実装への結合リフレクションによるプライベートメソッドのテスト(MethodInfo.Invoke、Python のgetattr、TS の(thing as any)、Java のField.setAccessible(true)、Ruby のObject#send、Rust の内部pub(crate)アクセス)。観測可能な動作ではなく、内部状態をアサートしている。ビジネス上の結果ではなく、協力オブジェクトに対するメソッド呼び出し回数を厳密に検証している。
広範すぎる例外アサーションAssert.ThrowsException<Exception>(...)(.NET) / pytest.raises(Exception) / メッセージマッチャーのないexpect(fn).toThrow(Error) / assertThrows(Exception.class, ...)(Java) / 種類を確認しないassert.Error(t, err) / クラスを指定しないexpect { ... }.to raise_error(RSpec) / expected = "..."のない#[should_panic] / -ExpectedMessageのないShould -Throw / EXPECT_THROW(stmt, SpecificType)ではなくEXPECT_ANY_THROW。
弱い変換オラクル正規化、大文字小文字の変換、トリミング、マッピング、変換のテストに、すでに期待する形式になっている入力を与えているため、アサーションでほかの不具合を検出できる場合でも、何もしない実装が通過してしまいます。変化が必要な入力を使用し、独立して導出した期待値をアサートしてください。生成側と消費側の往復テストは有用ですが、両側に同じ不具合が存在する可能性がある場合は、独立した形式アサーションの代わりにはなりません。
中程度 -- 保守性と明瞭さに関する問題
アンチパターン確認するポイント
不適切な命名シナリオや結果を説明していない Test1、TestMethod、test のようなテスト名です。利用可能な場合は、読み込まれた拡張機能を使用してください。そうでない場合は、同じテストスイート内の既存の命名規則に従ってください。
マジック値arrange/assert に説明のない数値や文字列が含まれています。Assert.AreEqual(42, result) / assert result == 42 / expect(result).toBe(42) -- 42 は何を意味しているのでしょうか?
テストの重複本体がほぼ同じで、単一の入力値だけが異なるテストメソッドが3つ以上あります。パラメーター化するべきです。[DataRow]/[Theory]/[TestCase](.NET)、@pytest.mark.parametrize(pytest)、test.each / it.each(Jest/Vitest)、@ParameterizedTest + @ValueSource(JUnit 5)、@DataProvider(TestNG)、Go のテーブル駆動テスト、where / shared examples(RSpec)、#[rstest](Rust)、@ParameterizedTest + @MethodSource(Kotlin)、-ForEach / -TestCases(Pester)、INSTANTIATE_TEST_SUITE_P(GoogleTest)、SECTION / GENERATE(Catch2)、TEST_CASE_TEMPLATE(doctest)を使用してください。.NET で重複を詳細に分析するには、exp-test-maintainability を使用してください。注:異なる境界条件(例:ゼロと負の値)を扱う2つのテストは重複ではありません。異なるエッジケースを個別のテストにすることで、失敗の診断が明確になり、適切な方法となります。
巨大なテスト約30行を超えるテストメソッド、または一度に複数の動作をテストするメソッドです。失敗した場合の診断が困難です。
アサーションを繰り返すアサーションメッセージAssert.AreEqual(expected, actual, "Expected and actual are not equal") / assert x == y, "x is not equal to y" / assertEquals(x, y, "values not equal") は情報を追加しません。メッセージではビジネス上の意味を説明してください。
AAA / Given-When-Then の分離不足Arrange/Act/Assert(BDD フレームワークの RSpec、Kotest behavior specs、Pester では Given/When/Then)の各フェーズが混在している、または区別できません。
低程度 -- スタイルと整理整頓
アンチパターン確認するポイント
未使用のテストインフラストラクチャ何もしないセットアップ/ティアダウンフック([TestInitialize]/[SetUp]/[BeforeEach]、setUp/@BeforeEach/@BeforeAll、beforeEach/beforeAll、before(:each)/before(:all)、BeforeEach/BeforeAll(Pester)、setUpWithError(XCTest))や、呼び出されていないテストヘルパーメソッドです。
リソースの未管理テストで、クリーンアップせずに破棄可能/クローズ可能なリソースを作成しています。.NET では using を使わない HttpClient/Stream、Python では with ブロックまたは try/finally を使わないファイル/接続、Java では try-with-resources を使わない FileInputStream、Go では記述のない defer file.Close()、Ruby では ensure を使わない接続、Rust では Drop に依存していない/close を忘れている場合、また、どの言語でも一時ファイル/DB のティアダウンが不足している場合です。
print によるデバッグテストの開発中に使用した Console.WriteLine / Debug.WriteLine / print() / console.log / System.out.println / fmt.Println / puts / dbg! / Write-Host / std::cout が残っています。
命名規則の不統一同じテストクラス/モジュール/ファイルで、異なる命名スタイルが混在しています(例:Method_Scenario_Expected を使うものと、ShouldDoSomething を使うものがある)。
ステップ 4:重大度を誠実に評価する

報告する前に、各指摘を重大度のルールに照らして再確認してください。

  • Critical/High:テストに誤った安心感を与える、または信頼性を損なう問題に限ります。正しさに関係なく常に成功するテストは Critical です。共有可変状態は、潜在的な分離リスクがある場合は High ですが、ユーザーが実際の実行順依存の失敗を報告した場合、またはコードから別のテストを先に実行する必要があると証明できる場合は Critical です。非同期アサーションの await 忘れは Critical(気付かれないまま成功)です。
  • Medium:保守性を実際に損なう問題に限ります。ほぼ同一のテストが 5 件以上ある場合や、Test1/test/it1 のように本当に意味のない名前が付いている場合です。
  • Low:見た目上の命名の不一致、軽微なスタイル上の好み、改善の余地があるアサーションメッセージです。迷った場合は Low と評価してください。
  • 呼び出し側の重大度の語彙を一貫して使用してください。 呼び出し側が Critical/Warning/Info を指定した場合、潜在的な信頼性リスクは Warning、保守性や見た目に関する懸念は Info に対応させてください。誤った安心感が実証されている場合や、現に発生している実行順依存の根本原因は Critical のままにしてください。要求されたすべての段階を埋めるためだけに格下げしてはいけません。重大度は指摘の説明量ではなく、実証された失敗の種類を表します。
  • 全体的な指摘とその個別事例を分けてください。 ファサード全体にまたがるカバレッジの問題は、影響を受けるすべてのテストを根拠として示す 1 件の Critical な全体指摘です。最後のファサードメソッドを含め、アサーションのない事例はすべて同じ誤った安心感の重大度を維持します。「1 件の指摘/影響を受けるテスト 6 件」と報告し、6 件の指摘に加えて 7 件目の要約指摘を追加してはいけません。また、重大度の段階を複数埋めるために、ある事例だけを格下げしてはいけません。
  • 通常の未テストケースをアンチパターンとして重大度評価しないでください。 隣接する未テスト分岐、例外パス、境界条件は、有用なカバレッジの機会になり得ますが、弱い既存テストがその不足を生んでいるのでない限り、アンチパターンの件数とは分けて記載してください。テストスイートに全体的な Critical の問題があるからといって、それらが Critical になるわけではありません。
  • 問題ではないもの(言語ごとの注意点):
    • Go と Rust のサブテスト付きテーブル駆動ループ(t.Run/for case in cases { ... })は慣用的な書き方であり、「条件付きテストロジック」ではありません。指摘しないでください。
    • pytest の bare assert は標準的なアサーション形式であり、アサーションライブラリの不足ではありません。指摘しないでください。
    • Go テストの if got != want { t.Errorf(...) } は標準的な等値比較です。アドホックな比較として指摘しないでください。
    • ゼロ、負の値、null など、異なる境界条件ごとにテストを分けること。重複として指摘しないでください。
    • [TestInitialize]/beforeEach の代わりに各テストで明示的にセットアップすること(分離性が高まります)。
    • 短く明快であり、理論上は統合できるテスト。
    • 自明でない入力を使ったラウンドトリップやシリアライズの等価性。これは有効なメタモルフィックな根拠であり、自己比較ではありません。ただし、生成側と消費側が同じ不具合を共有する可能性がある場合は、独立した表現による検証を推奨してください。
    • 変換済みの入力だけで検証されている変換処理。トートロジーの件数には含めず、変換を取り除いてもテストが成功するなら、判定基準が弱い点を報告してください。変換が必要となる入力を使い、独立して期待される出力を明示してください。
    • クローンの値の等価性。これは維持し、契約上ディープコピーが保証される場合は、参照の別個性や変更の独立性を示す根拠も追加してください。
    • パススルーが本番環境の契約である場合に、元の値を返すバリデーターやアクセサー。無効な入力ケースの不足はカバレッジの不足であり、既存のアサーションがトートロジーである証拠ではありません。

重要:テストの品質が高い場合は、冒頭ではっきりとそう伝えてください。レビューを正当化するために重大度を誇張してはいけません。Critical/High の問題がなく、軽微な Low の提案だけを見つけるレビューも、有効で価値があります。まずテストの優れた点を述べてください。

ステップ 5: 調査結果を報告する

深度の基準 — 支援なしのレビューよりも浅い、きれいにまとまった報告は失敗です。 書き始める前に、次の 5 項目すべてを満たしてください。

  1. 対象範囲内のすべてのテストを説明に含める。 要約する前に、メソッド/フィールドの完全な一覧を作成してください。 カバレッジの到達など体系的なパターンについては、 代表的な例を挙げるのではなく、影響を受けるすべてのテストを少なくとも一度は列挙してください。 テストを黙って飛ばしている調査結果の表(または未使用の static HttpClient フィールドのような フィクスチャ)は不完全です。レビューした件数を明記してください。
  2. オラクルを判定する前に、本番のコントラクトを検証する。 実際の変換、DTO のフィールド、および約束された同一性/クローンのセマンティクスを確認してください。 フィールドを勝手に作ったり、本番が意図的に非可逆である場合に可逆的な ラウンドトリップを要求したりしないでください。 疑わしい等価比較ごとに、所見を割り当てる前に、独立して既知のオラクルを書き留めてください。 そのアサーションが、変換後の出力を自明でない入力、クローンされた状態、スナップショット、 モック検証、またはフレームワークネイティブのアサーションコンテキストと比較している場合は、 それをトートロジー的またはアサーションなしと呼ぶ前に、なぜ失敗しうるのかを説明してください。 逆に、変換テストがすでに正規化された入力を使用している場合は、 その入力では変換と no-op を区別できないことを指摘し、変化する入力と 正確な期待出力を提示してください。プロデューサー/コンシューマーのペア API については、 ラウンドトリップテストを維持し、有効なメタモルフィックな証拠を置き換えるのではなく、 独立した表現のオラクルを 1 つ追加してください。
  3. すべての Critical/High の修正を完全かつ具体的にする。 正確な期待値(計算された割引、正確な CSV 行、完全な期待オブジェクト)を含む 置き換えのアサーションを示してください。// assert something here のような プレースホルダーは不可です。
  4. ミューテーション分析に広げることなく、明らかな隣接ギャップを挙げる — 本番コードが提供されている場合は、直接関連する未テストの throw、null 結果、 境界値、およびラウンドトリップ/カルチャー依存のリスクを 隣接するカバレッジギャップセクションに記載してください。網羅的な 分岐ごとの振る舞いのギャップには test-gap-analysis を使用してください。
  5. 報告を一貫した状態に保つ。 要約の件数は、列挙した所見と一致しなければなりません。 確定した結論を公開してください。再考はすべて書く前に行い、 「待って、それは間違っている」/「これは失敗するはずなのに失敗しない」といった 推論を出力に残さないでください。
  6. 非所見を断定的にする。 クリーンな、またはほぼクリーンな小さなスイートについては、 問題ないと判断した疑わしい構文と、それぞれを妥当にするフレームワークのルールを 挙げてください。クリーンな判定を、一般的なチェックリストや 憶測に基づく改善提案の下に埋もれさせないでください。

所見は次の構成で提示してください。

  1. 要約 -- 検出された問題の合計を、重大度(Critical / High / Medium / Low)別に分類して示します。テストが適切に書かれている場合は、その評価を先頭に述べてください。
  2. Critical および High の所見 -- それぞれ次の項目とともに列挙します:
    • アンチパターン名
    • 具体的な位置(ファイル、メソッド名、行)
    • なぜ問題なのかの簡単な説明
    • 具体的な修正(役立つ場合は修正前/後のコードを示す)
  3. Medium および Low の所見 -- ユーザーが詳細を希望しない限り、表にまとめます
  4. 良い点 -- テストがうまくやっている点(sealed クラス、具体的な例外型、データ駆動テスト、明確な AAA 構造、フェイクの適切な使用、適切な命名)を挙げてください。否定的な点だけを報告しないでください。

公開する前に、各所見に安定した識別子を割り当ててください。グループ化された行は、 いくつのメソッドを列挙していても 1 件の所見として数えます。別々の行は別々に数えます。 それらの行から要約を再計算してください。affected tests は別の数値として保ち、 まとめられた所見が隠れた件数の不一致を生み出さないようにしてください。

ステップ 6: 推奨事項を優先順位付けする

所見が多い場合は、どれを先に修正すべきかを推奨してください:

  1. Critical -- 直ちに修正してください。これらのテストは誤った自信を与えている可能性があります
  2. High -- 早めに修正してください。これらは不安定さやメンテナンス負担の原因になります
  3. Medium/Low -- 関連する編集の際に機会を捉えて修正してください

検証

  • 対象範囲内のすべてのテストメソッドが確認されている(レビューした件数を明記し、黙って除外されたものがない)
  • ID と往復変換に関する所見が本番環境の契約と一致し、実在するフィールドのみを使用している
  • すべての所見に具体的な場所が記載されている(一般的な警告だけではない)
  • すべての Critical/High の所見に、期待される正確な値を含む具体的な修正案がある
  • 隣接する未テストのエラーパスと境界値について指摘している
  • 要約の件数が列挙された所見と一致している
  • 所見をグループ化する場合、所見数と影響を受けるテスト数を区別している
  • 隣接するカバレッジの改善機会を、Critical なアンチパターンの所見として過大に扱っていない
  • レポートがすべてのカテゴリ(アサーション、分離、命名、構造)を網羅している
  • 問題点と併せて、肯定的な点も記載している
  • 推奨事項を重大度順に優先付けしている

よくある落とし穴

落とし穴解決策
スタイル上の問題を Critical として報告する命名と書式の問題は Medium/Low であり、Critical ではありません
対象を絞った修正ではなく、書き直しを提案する最小限の差分を示します。テスト全体ではなく、アサーションを変更してください
意図的な設計上の選択を問題として指摘するThread.Sleep/time.sleep/time.Sleep が実際のタイミングをテストする統合テストに含まれている場合、それはアンチパターンではありません。文脈を考慮してください
問題のないコードに誤検知をでっち上げるテストがベストプラクティスに従っているなら、そのように述べてください。「Critical 0 件、High 0 件、Low 1 件」というレビュー結果もまったく問題ありません。レビューを正当化するために所見を水増ししないでください
別々の境界値テストを重複として指摘するゼロ入力と負の入力をテストする2つのテストは、それぞれ異なるエッジケースをテストしています。本文が完全に同一で値だけが異なるテストが3件以上ある場合に限り、重複として指摘してください
見た目だけの問題を Medium と評価する命名の不一致(例:メソッド名は ArgumentException を示しているが、アサーションは ArgumentOutOfRangeException を確認している)は Medium ではなく Low です。テスト自体は正しく動作します
テストフレームワークを考慮しない言語拡張機能から読み込んだフレームワークの用語を使用してください。pytest のテストスイートを MSTest の用語で説明してはいけません
木を見て森を見落とすテストの80%にアサーションがない場合、個々のケースをすべて列挙するのではなく、まずその体系的な問題を取り上げてください
整然さのために詳細さを犠牲にする重大度の表や肯定的な所見を記載しても、すべてのテストの網羅、修正案における正確な期待値、隣接するエラーパスや境界値の不足を網羅したことにはなりません
レポート内で矛盾するまず理由を検討し、その後、各所見について確定した結論を1つだけ記載してください。「待って、これは間違いです」/「失敗するはずなのに失敗しません」といった再考を示してはいけません
件数が合わない重大度ごとの要約件数は、列挙した所見の件数と一致していなければなりません

コメント