unittestのmockでハマった3つのこと
外部ライブラリをpatchするとき
- nltkなど外部ライブラリのメソッドを呼び出す関数があったとする。
# ./nltkclient.py
from nltk.tag import pos_tag
def normalize():
morph = [...]
pos_tag(morph)
...
- この関数をテストするときにnltk.tag.pos_tagメソッドについてはモックをパッチさせてテストを実行したい。
# ./test_nltkclient.py
from unittest import TestCase
from unittest import mock
class NltkClient(unittest.TestCase):
# ↓パッチした。
@mock.patch('nltk.tag.pos_tag')
def test_normalize(self, pos_tag):
pos_tag.return_value = [('word', 'tag')]
- しかし、この書き方だとpos_tagのモックはただしくパッチされませんでした。
- 外部ライブラリをモックとして扱うには以下のように書く必要があった。
# ./test_nltkclient.py
class NltkClient(unittest.TestCase
× @mock.patch('nltk.pos_tag')
@mock.patch('nltkclient.pos_tag')
def test_normalize(self, pos_tag):
pos_tag.return_value = [('word', 'tag')]
return_valueの値がおかしい??
- 以下のように複数のオブジェクトをパッチするとき
@mock.patch('tasks.pos_tag')
@mock.patch('tasks.tokenize')
@mock.patch('tasks.normalize')
def test_normalize(self, pos_tag, tokenize, normalize):
pos_tag.return_value = [('mocked', 'VERB'), ('text', 'NOUN')]
tokenize.return_value = ['mocked' ,'text']
normalize.return_value = None
-
return_valueの値が正しくならない。
-
ここで注意したいのは、デコレータは内部から引数に入っていることです。したがって、引数はデコレータを書いた順と逆にする必要があることです。
@mock.patch('tasks.normalize')
@mock.patch('tasks.tokenize_text')
@mock.patch('tasks.pos_tag')
def test_normalize(self, pos_tag, tokenize_text, normalize):
pos_tag.return_value = [('mocked', 'VERB'), ('text', 'NOUN')]
tokenize_text.return_value = ['mocked' ,'text']
normalize.return_value = None
クラスのインスタンスをモックさせる
- 例えば、Celeryというタスクキューライブラリを使っていてAsyncResultはタスクの状態を監視できるクラスである。
- テストでは、実際にタスクを走らせずに実行されているかどうかのみをテストしたいとする。
- AsyncResultはtask.idをイニシャライザ引数として受け取りインスタンス化させる。属性としてstatusとresultを持ちそれぞれが返す値をモックさせる。
from unittest import mock
from rest_framework import status
from rest_framework.test import APITestCase
from apiv1.views import AsyncResult
class resultTrackViewTest(APITestCase):
# ↓ @mock.patch.object(クラス, メンバ, 返す値(メンバがメソッドならcallableである必要がある)
@mock.patch.object(AsyncResult, '__init__', lambda self, task_id: None)
@mock.patch.object(AsyncResult, 'status', 'SUCCESS')
@mock.patch.object(AsyncResult, 'result', 'write_path')
def test_get_response(self):
response = self.client.get(f'/api/v1/tasks/{uuid.uuid4()}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, 'write_path')
- 特に__init__メソッドについては、selfとtask_idを引数に受け取りNoneを返すラムダを指定することによってうまく動作しました。
- ふたつ前の章同様にAsyncResultはcelery.result.AsyncResultではなく、apiv1.views.AsyncResultなどそのライブラリを呼び出しているパスに対してモックを適用するように記述することに注意する。
参考にした記事
Author And Source
この問題について(unittestのmockでハマった3つのこと), 我々は、より多くの情報をここで見つけました https://qiita.com/s17161278/items/b7f302797ff750d3089c著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .