mockを理解するまでpytestがかなり苦痛だった
目次
はじめに
Pythonでpytest(Pythonのテストフレームワーク)を使い始めた頃、正直かなり苦手意識がありました。
特に辛かったのが「mock(モック)」です。
最初は、
「なんでこんな複雑なことしてるんだ?」
と思っていました。
普通に関数を呼べばいいように見えるのに、
- patch
- MagicMock
- return_value
- side_effect
など、見慣れないものが大量に出てきます。
しかも、エラーが出ても原因が分かりにくいです。
私はAWS Lambdaの開発でpytestを書くことが多かったのですが、S3やDynamoDBなどAWSサービスとの連携があるため、mockを避けて通れませんでした。
今回は、mockを理解するまでに苦労したことや、「ここを理解して少し楽になった」というポイントについて書いてみたいと思います。
最初は「なんでわざわざmockするの?」状態だった
最初の頃は、
「実際に動かせばよくない?」
と思っていました。
例えば、S3へアップロードする処理があるなら、
- 実際にS3へアップロード
- Lambdaを実行
- 結果確認
すればいいように感じていました。
ただ、実務ではこれを毎回やるのはかなり大変です。
例えば、
- テストが遅い
- AWS環境必要
- 権限問題がある
- コストがかかる
- ネットワーク依存になる
などがあります。
さらに異常系確認になるともっと大変です。
例えば、
- S3アップロード失敗
- DynamoDB更新失敗
- タイムアウト
- 例外発生
などを毎回本当に再現するのは現実的ではありません。
そこで使われるのがmockでした。
mockを使うことで、
「実際にはAWSへアクセスせず、擬似的に動作させる」
ことができます。
patch対象が分からなかった
mock初心者の頃、特に苦労したのが patch です。
例えば、
@patch("boto3.client")
のようなコードがあります。
ただ最初は、
「なんでここ文字列なん?」
状態でした。
しかも、patch対象を少し間違えるだけで動きません。
例えば、
@patch("boto3.client")
では動かず、
@patch("sample_module.boto3.client")
で動くことがあります。
最初はこの違いが全然分かりませんでした。
実際には、
「どこでimportされたものを差し替えるか」
が重要なのですが、初心者の頃はかなり混乱しました。
MagicMock地獄
mockを書いていると、よく MagicMock が出てきます。
ただ最初は、
「これ何者なん?」
という感じでした。
しかも設定不足だと、
<MagicMock name='mock.xxx'>
みたいな値が大量に出てきます。
特に辛かったのが、戻り値のmockです。
例えば、
mock_client.return_value.get_item.return_value
のようなコード。
最初見た時、
「return_value多すぎるだろ…」
と思いました。
さらにネストが増えるとかなり読みにくくなります。
context managerのmockがかなり難しかった
個人的にかなり苦戦したのが、with 文のmockです。
例えば、
with pd.ExcelWriter(path) as writer:
のような処理。
これをmock化する際、
mock_writer.return_value.__enter__.return_value
のような書き方が必要になります。
最初は意味が分かりませんでした。
「なんで __enter__ とか出てくるんだ…」
とかなり混乱しました。
ただ後から調べると、with 文は内部的に __enter__ と __exit__ を呼んでいることを知りました。
こういうPython内部仕様を知るきっかけになったのは面白かったです。
side_effectを理解してかなり楽になった
mockを使う中で、かなり便利だと思ったのが side_effect です。
例えば、
mock_s3.upload_fileobj.side_effect = Exception("upload error")
と書くと、意図的に例外を発生させられます。
これにより、
- 異常系テスト
- リトライ確認
- エラーハンドリング確認
などがかなりやりやすくなりました。
実務では正常系より異常系の方が重要なことも多いので、これはかなり助かりました。
最初はmockを「面倒なもの」と思っていましたが、理解してくると、
「現実では再現しづらいケースを簡単に試せる」
便利さを感じるようになりました。
mockを書き始めてから設計を見る目も変わった
面白かったのが、mockを書いていると「テストしやすい構造」を意識するようになったことです。
例えば、
- 関数が長すぎる
- AWS依存が強すぎる
- 責務が混ざっている
コードは、mockもかなり書きづらいです。
逆に、
- 処理分割されている
- 外部依存が切り離されている
- 関数責務が明確
なコードはテストもしやすいです。
つまり、mockを書いていると自然と設計改善にもつながっていきます。
これは実際にやってみて気づいた部分でした。
まとめ
pytestを触り始めた頃、mockはかなり苦痛でした。
特に最初は、
- patch意味不明
- return_value多すぎ
- MagicMock怖い
- エラー読めない
という状態でした。
ただ、実務でAWS連携や外部サービス処理を扱う中で、mockは避けて通れませんでした。
そして少しずつ理解していくと、
- 外部依存を切り離せる
- 異常系を簡単に試せる
- 手動試験を減らせる
- 安心して修正できる
というメリットがかなり大きいことに気づきました。
今でもmockは簡単だとは思っていません。
ただ、「最初はみんな苦戦するものなんだな」とは感じています。
もし今pytestやmockで苦しんでいる人がいれば、まずは
「全部理解しようとしすぎない」
ことをおすすめしたいです。
実際、私自身も実務で使いながら少しずつ慣れていきました。



















