Python テストの「嘲笑地獄」から逃れるための 7 つの実証済みテクニック
はじめに
Python の unittest.mock
ライブラリに不満がありますか? テストではまだ実際のネットワーク呼び出しを行ったり、紛らわしい AttributeError
メッセージをスローしたりしますか? 「嘲笑地獄」とも呼ばれるこの一般的な問題は、テストの速度が遅く、信頼性が低く、維持が困難なテストを引き起こします。この投稿では、高速で信頼性の高いテストにモックが不可欠である理由を説明し、依存関係を効果的にパッチ、モック、分離して「モッキングの健全性」を確保するための7 つの実践的な戦略を提供します。 これらのテクニックは、Python テストの経験に関係なく、ワークフローを合理化し、堅牢なテスト スイートを作成します。
課題: 単体テストにおける外部依存関係
最新のソフトウェアは、データベース、ファイル システム、Web API などの外部システムと頻繁にやり取りします。これらのやり取りが単体テストに浸透すると、次のような問題が発生します。
AttributeError
メッセージや部分的なモックが生成されます。開発者、QA エンジニア、プロジェクト マネージャーはすべて、よりクリーンで信頼性の高いテストから恩恵を受けます。 テストがランダムに失敗したり、実際のサービスにアクセスしたりすると、CI/CD パイプラインが中断され、開発が遅れます。 外部依存関係を効果的に分離することが重要です。 しかし、よくある落とし穴を回避しながら、正しいモックを確保するにはどうすればよいでしょうか?
「地獄の嘲笑」を避けるための 7 つのハック
次の 7 つの手法は、テストを効率的、正確かつ迅速に行うためのフレームワーク (「健全性をモックする」チェックリスト) を提供します。
一般的なエラーは、関数が呼び出される場所ではなく、関数の定義でパッチを適用することです。 Python はテスト対象のモジュール内のシンボルを置き換えるので、そのモジュールのインポート コンテキスト内でパッチを適用する必要があります。
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
@patch("some.lib.foo")
@patch("my_module.foo")
パッチ my_module.foo
を適用すると、テストで使用される箇所で確実に置換されます。
個々の関数/クラス、またはモジュール全体を置き換えることができます。
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
MagicMock
に置き換えます。 すべての関数/クラスはモックになります:<code class="language-python">from unittest.mock import patch with patch("my_module.foo") as mock_foo: mock_foo.return_value = "bar"</code>
コードで他の my_module
属性を呼び出す場合は、mock_mod
で定義するか、AttributeError
に直面します。
トレースバックは誤解を招く可能性があります。 重要なのは、コードが関数をどのようにインポートするかです。常に:
my_module.py
)。<code class="language-python">with patch("my_module") as mock_mod: mock_mod.foo.return_value = "bar" # Define all attributes your code calls!</code>
または
<code class="language-python">from mypackage.submodule import function_one</code>
sub.function_one()
が表示された場合は、"my_module.sub.function_one"
にパッチを当ててください。from mypackage.submodule import function_one
が表示された場合は、"my_module.function_one"
にパッチを当ててください。外部リソース (ネットワーク リクエスト、ファイル I/O、システム コマンド) への呼び出しをモックアウトして、次のことを行います。
たとえば、関数がファイルを読み取る場合:
<code class="language-python">import mypackage.submodule as sub</code>
テストでパッチを適用します:
<code class="language-python">def read_config(path): with open(path, 'r') as f: return f.read()</code>
外部リソースを処理するメソッド全体をモックするか、個々のライブラリ呼び出しにパッチを適用します。 確認する内容に基づいて選択してください。
<code class="language-python">from unittest.mock import patch @patch("builtins.open", create=True) def test_read_config(mock_open): mock_open.return_value.read.return_value = "test config" result = read_config("dummy_path") assert result == "test config"</code>
<code class="language-python">class MyClass: def do_network_call(self): pass @patch.object(MyClass, "do_network_call", return_value="mocked") def test_something(mock_call): # The real network call is never made ...</code>
高レベルのパッチは高速ですが、内部メソッドのテストは省略されます。低レベルのパッチではより細かい制御が可能ですが、より複雑になる可能性があります。
モジュール全体にパッチを適用すると、デフォルト属性のない MagicMock()
になります。コードが次を呼び出す場合:
<code class="language-python">@patch("my_module.read_file") @patch("my_module.fetch_data_from_api") def test_something(mock_fetch, mock_read): ...</code>
テスト内:
<code class="language-python">import my_service my_service.configure() my_service.restart()</code>
属性の定義を忘れると、AttributeError: Mock object has no attribute 'restart'
.
コールスタックが複雑すぎる場合は、より深いインポートに到達しないように高レベル関数にパッチを適用します。例:
<code class="language-python">with patch("path.to.my_service") as mock_service: mock_service.configure.return_value = None mock_service.restart.return_value = None ...</code>
テストする必要がない場合complex_operation
:
<code class="language-python">def complex_operation(): # Calls multiple external functions pass</code>
これによりテストが高速化されますが、complex_operation
の内部のテストがバイパスされます。
影響と利点
これらの「健康を嘲笑する」戦略を適用すると、次の結果が得られます。
AttributeError
および同様の問題が最小限に抑えられます。これらのプラクティスを使用しているチームは、多くの場合、より信頼性の高い CI/CD パイプライン、デバッグの削減、より効率的な機能開発を実現します。
<code class="language-python"># my_module.py from some.lib import foo def do_things(): foo("hello")</code>
この図は、正しいパッチ適用によって外部呼び出しがどのようにインターセプトされ、テストがよりスムーズになるかを示しています。
今後の検討事項
Python のモックは強力です。 以下を考慮してください:
pytest-mock
は簡素化された構文を提供します。今すぐテスト スイートを改善しましょう! これらのテクニックを適用して、結果を共有してください。 Python プロジェクトで優れた「モッキング ヘルス」を維持しましょう!
以上がPython テストでの「嘲笑地獄」を回避するための実践的なハックの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。