単体テストが不可能な関数やクラス、およびその理由


21

開発者からの優れたユニットテストがないことの主な言い訳は、「コードはユニットテスト可能な方法で設計されていない」です。ユニットテストできないデザインとコードの種類を理解しようとしています。


2
言い訳?同僚?マネージャー?どの言語/フレームワークを使用していますか?

1
アプリケーションには多くのレガシーコードがあり、再設計する時間はありません。
knut

4
@gnat:私は同意しません。あなたが引用した質問は、単体テストが役に立たない状況に関するものです。現在の質問は、単体テストを困難にする状況についてです。
アルセニムルゼンコ

@MainMaは、さまざまな質問を読んでいるようです。「ユニットテストできない設計とコードの種類を理解しようとしています。」=>「単体テストを行わないのが適切なのはいつですか?」
gnat

1
@manizzzz:質問にそれを編集したいかもしれません。
jmoreno

回答:


27

いくつかの要因により、コードの単体テストが困難になる場合があります。この場合、リファクタリングは、コードをテスト可能にするためにコードを改善するのに役立ちます。

おそらくテストが難しいコードの例:

  • 1000-LOC関数、
  • グローバル状態に大きく依存するコード、
  • インターフェイスや依存性注入に依存するのではなく、データベースコンテキストなどのオブジェクトを構築するために具体的で複雑なコードが必要な場合、
  • ゆっくり実行するコード、
  • スパゲッティコード、
  • 読みやすさや保守性を気にせずに何年も修正されたレガシーコード
  • 著者の本来の意図についてのコメントやヒントを持っていないコード(のような変数名を使用する例のコードを理解することは困難function pGetDp_U(int i, int i2, string sText)

ユニットテストはコードの小さな部分に関するものであるため、明確なアーキテクチャがなくてもコードをユニットテストすることは難しくありません。不明瞭なアーキテクチャは、統合およびシステムテストに依然として悪影響を及ぼします。


8
また、それはない乱数のような非純粋関数の注入依存関係、現在の時刻、ハードワイヤードI / Oなどを行うテストコードには難しい
9000

そのようなコードをテストするのは簡単です-適切なテストツールが必要なだけで、コードをそれらに合わせて変更する必要はありません。例としてMicrosoft Fakesを試してください。
gbjbaanb

@MainMa、私はこの答えが好きです。また、統合テストやシステムテストにさまざまなテストを推進する要因について、少しコメントしていただけますか?過去の質問と同じような質問をした理由は、どの種類のテストをどこに配置するのが最適かを説明するロードマップを持っていなかったためです(おそらく、最も費用対効果の高い場所に配置する)単体テストは唯一のものでした。
Jトラナ14年

14

コードの単体テストを困難にする多くのことがあります。偶然にも、これらの多くは、コードの保守を困難にします。

  • デメテル違反の法則
  • 依存関係注入する代わりに、メソッド内にオブジェクトを作成します。
  • 密結合。
  • 結束が悪い。
  • 副作用に大きく依存しています。
  • グローバルまたはシングルトンに大きく依存しています。
  • 多くの中間結果を公開しません。(かつて、単一の出力で利用可能な中間結果のない10ページの長さの数学関数を単体テストしなければなりませんでした。私の前任者は、基本的にコードの答えをハードコードしました)。
  • データベースのように、モックが困難なサービスに大きく依存しています。
  • ランタイム環境は、組み込みターゲットなどの開発環境とは大きく異なります。
  • コンパイルされた形式でのみ使用可能なユニット(サードパーティのDLLなど)。

これは素晴らしい答えだと思います。多くのコードレベルの問題、およびグローバルな状態の問題に触れます。@MainMaには、妥当と思われる他の問題がいくつかありますが、定義はそれほど明確ではありません。Jeffery ThomasはI / OとUIについて言及しています。これらの3つの答えの良い部分を追加すれば、まとまりのある応答が得られると思います。しかし、コードのアンチパターンに焦点を当てているため、この回答が最も好きです。
M2tM

1
Argh-ビジネス要件に類似せず、特定の時点での出力である単体テストのアサートよりも悪いことはありません-たとえば、3回呼び出されるようにセットアップされたモック?なぜ3 テストが最初に実行されたのは3だったため/ rant :)
Michael

密結合は、不適切な場合にのみ悪くなります。結束性の高いコードでの密結合は必要です。たとえば、変数宣言とそれに続く使用。密結合、非常に凝集性。
ダイエット仏14年

1
ビジネスケース/正当化なしで、コードが実行したことに対して出力がチェックされる単体テストは、特性テストと呼ばれます。以前はテストがなく、多くの場合文書化された要件がなかったメンテナンスで使用され、その機能の出力が変更された場合に破損する何かを挿入する必要があります。彼らは何もないよりはましです。
アンディクルーウェル

5

人々が単体テストを望まないコードの一般的な例:

  • I / Oと直接やり取りするコード(ファイルの読み取り、直接ネットワーク呼び出しなど)。
  • UIを直接更新するコード。
  • シングルトンまたはグローバルオブジェクトを直接参照するコード。
  • オブジェクトまたはサブオブジェクトの状態を暗黙的に変更するコード。

モックフレームワークを使用すると、これらの例はすべてユニットテストできます。内部依存関係の模擬置換をセットアップするだけです。

本当にユニットテストできないもの:

  • 無限ループ(スレッドマネージャー、ドライバー、またはその他の長時間実行コードの場合)
  • 特定の種類の直接アセンブリ操作(一部の言語がサポート)
  • 特権アクセスを必要とするコード(不可能ではない、良い考えではない)

2

ユニットテストの作成がより困難になる可能性があるいくつかの領域があります。ただし、テストが複雑になる可能性があるため、便利なテクニックを手に負えないという意味ではないことを強調します。他のコーディング同様に、利益がコストを上回るかどうかを判断するために独自の分析を行う必要があり、ランダムな男がネットに投稿したものを盲目的に受け入れないでください。

設計されたコードの不十分な記述

  • 不適切なカップリング(通常、本来あるべきではない密なカップリング)
  • キッチンシンクコード(関数のロジック/責任が多すぎる)

異なるスコープでの状態の信頼

これらのほとんどのコストは、自分が何をしているのかわからない限り制御不能です。残念ながら、多くの場合、テストの複雑さなどを軽減する方法でこれらの手法を使用する方法を知りません。

  • シングルトン
  • グローバル
  • 閉鎖

外部/システム状態

  • ハードウェア/デバイスの依存関係
  • ネットワークの依存関係
  • ファイルシステムの依存関係
  • プロセス間依存関係
  • その他のシステムコールの依存関係

並行性

  • スレッド化(ロック、クリティカルセクションなど)
  • 分岐
  • コルーチン
  • コールバック
  • シグナルハンドラー(すべてではなく、一部)

2

テストできないコードなどはありません。ただし、実際にはテストするのが非常に難しいコードの例がいくつかあります(おそらく努力する価値がないという点まで)。

ハードウェアの相互作用-コードがハードウェアを直接操作する(たとえば、物理デバイスを移動するためにレジスタに書き込む)場合、ユニットテストは非常に困難または高価になる可能性があります。テストに実際のハードウェアを使用すると、高価になって適切なフィードバックをテストハーネスに取得できます(さらに多くの機器があります!)、そうでない場合は、物理オブジェクトの正確な動作をエミュレートする必要があります。いくつかのインスタンス。

クロックの相互作用-通常、システムクロック機能を非常に簡単にモックアウトできるため、これは簡単です。しかし、できない場合、これらのテストは管理不能になります-リアルタイムに基づくテストは実行に時間がかかる傾向があり、私の経験では、システムの負荷が物事を必要以上に長くするため、非常に脆くなる傾向があります、ファントムテストの失敗の原因。


0

私の主な3つのグループは次のとおりです。

  • 外部サービスに依存するコード

  • テスターがアプリケーションとは無関係に状態を変更できないシステム。

  • 実稼働セットアップを複製しないテスト環境。

これは、開発者としてQAエンジニアになったときに最も経験したことです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.