test-anti-patterns
.NETのテストコードを対象に、信頼性と診断価値を損なうアンチパターンを検出・レビューするスキルです。アサーションの欠落・常に真となるアサーション・例外の握りつぶし・過剰なモック・テスト間の依存・マジックナンバー・カバレッジ水増しなど、テストが通っても何も検証していない問題を洗い出します。新規テストの作成やフレームワーク移行、テストの実行には対応していません。
description の原文を見る
> Detection-focused review of .NET test code for anti-patterns that undermine reliability and diagnostic value. USE FOR: audit test quality, review test code, find test anti-patterns, tests pass but don't verify anything, flaky tests, ordering dependency, duplicate tests, magic values, missing/no assertions, swallowed exceptions, always-true assertions, over-mocking, test coupling, coverage touching, coverage inflation. DO NOT USE FOR: writing new tests (use writing-mstest-tests), direct MSTest API rewrites or implementation-only fixes such as swapped Assert.AreEqual argument order, running tests (use run-tests), migrating between frameworks (use migration skills), deep formal audit based on academic test smell taxonomy (use test-smell-detection).
SKILL.md 本文
テスト アンチパターン検出
.NET テストコードの迅速で実用的な分析を行い、テスト信頼性、保守性、診断値を損なわせるアンチパターンと品質問題を検出します。
使用すべき場合
- テスト品質をレビューするか、テスト臭を見つけたいと言っている
- テストがなぜ不安定または信頼できないのかを知りたい
- 「自分のテストは良いのか?」または「自分のテストの何が悪いのか?」と聞いている
- テスト監査またはテストコードレビューをリクエストしている
- 既存のテストコードを改善したい
使用すべきでない場合
- ユーザーがゼロからテストを新規作成したい場合(
writing-mstest-testsを使用) - テストコード内の MSTest を診断レビューではなく直接実装修正したい場合(
writing-mstest-testsを使用) Assert.AreEqualの引数順序の入れ替わりを修正したい場合(writing-mstest-testsを使用)DynamicDataをIEnumerable<object[]>からValueTupleに変換したい場合(writing-mstest-testsを使用)- テストを実行または実行したい場合(
run-testsを使用) - テストフレームワークまたはバージョン間でマイグレーションしたい場合(移行スキルを使用)
- コードカバレッジを測定したい場合(対象外)
- 学術的分類法と拡張カタログを使用した深い形式的なテスト臭監査をしたい場合(
test-smell-detectionを使用)
インプット
| インプット | 必須 | 説明 |
|---|---|---|
| テストコード | はい | 分析対象の 1 つ以上のテストファイルまたはクラス |
| 本番コード | いいえ | テストが何を検証すべきかのコンテキストのための、テスト対象のコード |
| 特定の懸念事項 | いいえ | レビューを狭めるための「不安定性」や「命名」などの焦点を絞った領域 |
ワークフロー
ステップ 1: テストコードを収集
ユーザーがレビューを望むテストファイルを読みます。ユーザーがディレクトリまたはプロジェクトを指している場合は、dotnet-test-frameworks スキルのフレームワーク固有マーカー(例:[TestClass]、[Fact]、[Test])を使用してすべてのテストファイルをスキャンします。
本番コードが利用可能な場合は、それも読んでください。これは、実装の詳細ではなく動作に結合されたテストを検出するために重要です。
ステップ 2: アンチパターンをスキャン
各テストファイルを以下のアンチパターンカタログに照らし合わせます。重大度でグループ化された検出結果を報告します。
重大 -- 誤った確信を与えるテスト
| アンチパターン | 確認事項 |
|---|---|
| アサーションなし | コードを実行するが何もアサートしないテストメソッド。アサーションなしのテストが合格することは何も証明しません。 |
| カバレッジ接触 | 型のすべてのパブリックメソッドを体系的に呼び出すテストクラス(多くの場合、アルファベット順または宣言順)で、意味のある成果をアサートしません。各テストは通常、var result = sut.MethodName(...) を実行し、アサーションがないか、単に Assert.IsNotNull(result) のみです。コード カバレッジ メトリクスを膨らませることが目的であり、動作を検証することではありません。単一のアサーション フリー テストとは異なり、パターンは実表領域の体系的なカバレッジで実際の検証はありません。 |
| 自己参照アサーション | 操作の出力がその入力と等しいことをアサートします。例:Assert.AreEqual(input, Parse(input.ToString())) または Assert.AreEqual(x, Identity(x))。テストは同語反復です。ラウンドトリップが壊れている場合にのみ失敗しますが、変換が実際に発生したことは確認しません。Assert.AreEqual(dto.Name, dto.Name)(フィールドをそれ自体に対してアサート)もキャッチします。 |
| 飲み込まれた例外 | try { ... } catch { } または catch (Exception) でリスローまたはアサーションなし。失敗が静かに隠れています。 |
| catch ブロック内のアサートのみ | try { Act(); } catch (Exception ex) { Assert.Fail(ex.Message); } -- 代わりに Assert.ThrowsException を使用します。例外がスローされない場合、結果が間違っていてもテストは合格します。 |
| 常に真のアサーション | Assert.IsTrue(true)、Assert.AreEqual(x, x)、またはページが決して失敗できない条件。 |
| コメント アウトされたアサーション | 無効になったが、テストは引き続き実行されるアサーション。カバレッジの幻想を与えます。 |
高 -- テストが苦痛を引き起こす可能性がある
| アンチパターン | 確認事項 |
|---|---|
| フラキネス インジケーター | Thread.Sleep(...)、Task.Delay(...) 同期用、抽象化なしの DateTime.Now/DateTime.UtcNow、シードなしの Random、環境に依存するパス。 |
| テスト順序の依存性 | テスト全体で変更される静的可変フィールド、完全に状態をリセットしない [TestInitialize]、スイート内で実行すると合格するが個別に実行すると失敗するテスト(またはその逆)。 |
| オーバーモッキング | モック セットアップ行が実際のテストロジックより多い。モック上の正確な呼び出しシーケンスを検証します。テストが所有する型をモックします。深いモック監査については、exp-mock-usage-analysis を使用します。 |
| 実装結合 | リフレクション経由のプライベートメソッドのテスト、内部状態でのアサーション、観察可能な動作の代わりにコラボレータの正確なメソッド呼び出し数の検証。 |
| 広い例外アサーション | Assert.ThrowsException<Exception>(...) 特定の例外型の代わりに。また:[ExpectedException(typeof(Exception))]。 |
中程度 -- 保守性と明確性の問題
| アンチパターン | 確認事項 |
|---|---|
| 命名が悪い | Test1、TestMethod のようなテスト名。シナリオまたは予想される結果を説明しない名前。良い例:Add_NegativeNumber_ThrowsArgumentException。 |
| マジック値 | 明確でない数値または文字列を配列/アサートで:Assert.AreEqual(42, result) -- 42 は何を意味するのか? |
| テストの重複 | 単一の入力値のみが異なるほぼ同一の本体を持つ 3 つ以上のテストメソッド。データ駆動型([DataRow]、[Theory]、[TestCase])である必要があります。詳細な重複分析については、exp-test-maintainability を使用します。注:ゼロと負の値など、異なる境界条件をカバーする 2 つのテストは重複していません。異なるエッジケースの個別のテストは、より明確な失敗診断を提供し、有効な実践です。 |
| 巨大なテスト | 約 30 行を超えるテストメソッドまたは複数の動作をテストします。失敗時の診断が困難です。 |
| アサーション メッセージがアサーションを繰り返す | Assert.AreEqual(expected, actual, "Expected and actual are not equal") は情報を追加しません。メッセージは業務上の意味を説明する必要があります。 |
| AAA 分離の欠落 | Arrange、Act、Assert フェーズがインターリーブまたは区別できない。 |
低 -- スタイルとクリーンネス
| アンチパターン | 確認事項 |
|---|---|
| 未使用のテストインフラ | [TestInitialize]/[SetUp] は何もしません。呼び出されないテストヘルパーメソッド。 |
| IDisposable が破棄されない | テストは HttpClient、Stream、またはその他の破棄可能なオブジェクトを using またはクリーンアップなしで作成します。 |
| Console.WriteLine デバッグ | テスト開発中に使用されたままの Console.WriteLine または Debug.WriteLine ステートメント。 |
| 命名規則の不一致 | 同じテストクラス内の命名スタイルのミックス(例:一部は Method_Scenario_Expected を使用し、他は ShouldDoSomething を使用)。 |
ステップ 3: 重大度を正直に調整
報告する前に、以下の重大度ルールに対して各検出結果を再確認します。
- 重大/高:テストが誤った確信を与えたり信頼できなくなったりする問題に対してのみ。テストが正確さに関係なく常に合格することは重大です。フレーキーな共有状態は高いです。
- 中程度:保守性に積極的に害を及ぼす問題に対してのみ -- 5 つ以上のほぼ同一のテスト、
Test1のような真に無意味な名前。 - 低:外観上の命名ミスマッチ、軽微なスタイル設定。不明な場合は低く評価します。
- 問題ではない:ゼロと負とnull などの異なる境界条件に対する個別のテスト。明示的なテスト単位のセットアップではなく
[TestInitialize](これは隔離を改善します)。短くて明確だが理論的には統合できるテスト。
重要:テストがよく書かれている場合は、明確にそれを述べてください。レビューを正当化するために重大度を膨らませないでください。ゼロの重大/高の問題を見つけ、低い提案のみを見つけるレビューは有効で価値のある成果です。テストが何をよくしているかから始めてください。
ステップ 4: 検出結果を報告
この構造で検出結果を提示します。
- 概要 -- 見つかった問題の合計数、重大度(重大/高/中程度/低)で分類。テストがよく書かれている場合は、その評価を主導してください。
- 重大および高い検出結果 -- リストで次のものを含む各検出結果:
- アンチパターン名
- 特定の場所(ファイル、メソッド名、行)
- 問題である理由についての簡潔な説明
- 具体的な修正(有用な場合は修正前後のコードを表示)
- 中程度および低い検出結果 -- ユーザーが完全な詳細を望まない限り、テーブルで要約
- 肯定的な観察 -- テストがよくしていることを呼び出す(シール済みクラス、特定の例外タイプ、データ駆動型テスト、明確な AAA 構造、フェイクの適切な使用、良い命名)。ネガティブのみを報告しないでください。
ステップ 5: 推奨事項を優先順位付け
多くの検出結果がある場合は、最初に修正すべき内容をお勧めします。
- 重大 -- 即座に修正、これらのテストは誤った確信を与えているかもしれません
- 高 -- すぐに修正、これらはフラキネスまたは保守負担を引き起こします
- 中程度/低 -- 関連する編集中に機会的に修正
検証
- すべての検出結果に特定の場所が含まれている(一般的な警告ではない)
- すべての重大/高い検出結果に具体的な修正が含まれている
- レポートはすべてのカテゴリ(アサーション、隔離、命名、構造)をカバー
- 肯定的な観察が問題とともに含まれている
- 推奨事項は重大度で優先順位付けされている
一般的な落とし穴
| 落とし穴 | 解決策 |
|---|---|
| スタイルの問題を重大として報告する | 命名とフォーマットは中程度/低であり、決して重大ではありません |
| 対象の修正の代わりに書き直しを提案する | 最小限のディフを表示します。テスト全体ではなくアサーションを変更します。 |
| 意図的な設計選択を悪質フラグ立てする | Thread.Sleep がテスト実際のタイミングをテストする統合テストにある場合、それはアンチパターンではありません。コンテキストを考慮してください。 |
| クリーンコードで誤検知を発明する | テストがベストプラクティスに従う場合は、そう述べてください。「0 重大、0 高、低 1」というレビューは完全に有効です。レビューを正当化するために検出結果を膨らませないでください。 |
| 個別の境界テストを重複としてフラグ立てする | ゼロと負の入力の 2 つのテストは異なるエッジケースをテストします。3 つ以上のテストが単一の値で異なる場合にのみ重複としてフラグ立てします。 |
| 外観の問題を中程度として評価する | 命名ミスマッチ(例:メソッド名は ArgumentException を言いますが ArgumentOutOfRangeException をアサート)は低いです。中程度ではありません -- テストは引き続き正しく機能します。 |
| テストフレームワークを無視する | xUnit は [Fact]/[Theory] を使用、NUnit は [Test]/[TestCase] を使用、MSTest は [TestMethod]/[DataRow] を使用 -- 正しい用語を使用 |
| 森を見失う | 80%のテストにアサーションがない場合、すべてのインスタンスをリストするのではなく、その体系的な問題を主導してください |
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- dotnet
- リポジトリ
- dotnet/skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/dotnet/skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。