2020年9月18日金曜日

pytest で組み込みライブラリが monkeypatch できない場合にチェックするポイント

概要

例えば socket モジュール内のメソッドを monkeypatch したい場合に「なぜか monkeypatch できない」という現象が発生したとします
そんな場合にチェックするべきポイントを紹介します

環境

  • macOS 10.15.6
  • Python 3.8.5

準備

  • pipenv install pytest

monkeypatch できないコード

まずは monkeypatch できないコードを紹介します
一見できそうなのですが以下で紹介しているテストコードでテストしようとすると普通に DNS サーバに問い合わせてしまいます

  • vim app.py
from socket import gethostbyname

def resolve(hostname):
    return gethostbyname(hostname)

テストコード

こちらがテストコードです
monkeypatch で socket の gethostbyname をパッチしています
リターンは適当な IP アドレスにしています

import socket
from app import resolve

def test_resolve(monkeypatch):
    monkeypatch.setattr(socket, 'gethostbyname', lambda hostname: "192.168.100.1")
    result = resolve('test.local')
    print(result)
    assert (result == "192.168.100.1")

テストの実行は以下のようにします

  • pipenv run pytest

すると socket.gaierror: [Errno 8] nodename nor servname provided, or not known というエラーが発生します
これは monkeypatch できずに実際に test.local を DNS に問い合わせているために名前解決できずエラーになっています

なぜ monkeypatch できないのか

どうやらロジック側で import している方法とテスト側で import しているやり方を統一しないとダメなようです
今回だと from socket import gethostbyname ではダメだということです
以下のようにロジック側を修正します

  • vim app.py
import socket

def resolve(hostname):
    return socket.gethostbyname(hostname)

これで再度テストを実行すると正常に終了するのが確認できると思います

最後に

monkeypatch で組み込みモジュールをテストする場合はロジック側とテスト側の import 方法を合わせる必要があるということがわかりました
なかなか気づきにくい仕様かなと思います
しかし知らないとハマるので知っておいて損はないと思います

0 件のコメント:

コメントを投稿