概要
例えばステータスを監視するようなコードがある場合にステータスが非同期で変わります
ただそのステータスのレスポンス情報を monkeypatch してしまうと一定のレスポンス情報しか返らなくなりステータスを監視するループから抜け出せなくなります
そんな場合に threading を使って monkeypatch を遅延して適用する方法を使えば monkeypatch を使ってもうまくループを抜け出せます
環境
- macOS 10.15.6
- Python 3.8.5
ステータスを監視するコード
例えば以下のようなステータスを監視するコードを考えます
これは while ループで 5 秒ごとにステータス情報をチェックし stopped 以外に慣ればループを抜けます
vim app.py
import time
def get_status():
return "stopped"
def check_status():
while get_status() == "stopped":
print("status is stopped")
time.sleep(5)
return "status is running"
この状態だと get_status
は常に stopped を返すためループを抜けれません
monkeypatch を使って別のステータスを返却するようにする
上記のコードを monkeypatch を使ってテストしループを抜け出せるようにしてみます
vim test_app.py
from app import check_status
def test_check_status(monkeypatch):
monkeypatch.setattr('app.get_status', lambda: "running")
result = check_status()
thread1.join()
assert (result == "status is running")
pipenv run pytest ./test_app.py -s
こんな感じで monkeypatch すれば簡単にループを抜けるテストが書けます
しかしここで問題なのはループ内の処理を一度も実行しないためカバレッジが上がりません
なので一回以上ループ内の処理をさせるように monkeypatch を遅延して適用したくなります
monkeypatch を遅延させるために threading を使う
monkeypatch を当てるための簡単なスレッドを作成すれば解決できました
テストのコードを以下のように書き換えます
import threading
import time
from app import check_status
def patch(monkeypatch):
time.sleep(15)
monkeypatch.setattr('app.get_status', lambda: "running")
def test_check_status(monkeypatch):
thread1 = threading.Thread(target=patch, args=(monkeypatch,))
thread1.start()
result = check_status()
thread1.join()
assert (result == "status is running")
15 秒後に monkeypatch が当たるように変更してみます
threading は必ず start -> join してメインのスレッドに戻ってくるようにしましょう
これで実行すると「status is stopping」が 3 回表示された後にちゃんとループを抜けてテストが正常終了するのが確認できると思います
最後に
monkeypatch + threading で遅延パッチする方法を紹介しました
非同期な処理をパッチする場合には使えるテクニックかなと思います
0 件のコメント:
コメントを投稿