Date post: | 25-Jun-2015 |
Category: |
Technology |
Upload: | toru-furukawa |
View: | 1,459 times |
Download: | 0 times |
Mock と patch
ふるかわとおる
お前、誰よ?
• ふるかわとおる – @torufurukawa – bucho と呼ばれています
• 株式会社バスキュール – 生放送テレビ番組と連動するサービス – エンジニア足りません
ユニットテスト
def test(): result = foo('wozozo') assert result == 'unko'
依存先…
def foo(name): f = urlopen('http://%s.com/' % name) raw = f.read() d = json.loads(raw) return d['data']
不確定
確定的な 結果に依存
ユニットテストしにくい例
• 戻り値が不確定 – 乱数 – 時刻
• セットアップがだるい – データベース – Web API
テスト対象の外側を入出力として扱う
def foo(name): raw = urlopen('http://…') d = json.loads(raw) return d['data']
よびだし HTTP req
HTTP resp 戻り値
ここも入出力
そこで mock モジュール ですよ
• Python 3.2 以前 PyPI – easy_install, pip, etc.
• Python 3.3 標準ライブラリ – uniDest.mock
1: 依存先オブジェクトを入れ替える
テスト対象 urlopen
テスト対象 mock
with patch(...) で入れ替える
from unittest.mock import patch def test(): with patch('urllib.request.urlopen')\ as m: result = foo('wozozo')
Mock オブジェクトと入れ替え
>>> with patch('urllib.request.urlopen')\ as m: ... from urllib.request import urlopen ... urlopen is m ... True
Mock オブジェクトはアクセスし放題
>>> m <MagicMock name='urlopen' id='1'> >>> m.read() <MagicMock name='urlopen.read()' id='2'> >>> m.hoge <MagicMock name='urlopen.hoge' id='3'>
@patch で入れ替える
@patch('urllib.request.urlopen') def test(m): result = foo('wozozo')
setUp と tearDown で patch
class MyTest(TestCase): def setUp(self): self.patcher = patch('...') self.m = patcher.start() def tearDown(self): self.patcher.stop()
2: 依存先の呼び出し履歴を確認
テスト対象 mock テスト
call_count で呼び出し回数を確認
@patch('urllib.request.urlopen') def test(m): result = foo('wozozo') assert m.call_count == 1
call_args で引数を確認
@patch('urllib.request.urlopen') def test(m): result = foo('wozozo') assert m.call_count == 1 assert (m.call_args == (('http://...',), {}))
*args と **kw が返ってくる
m(a, b, x=1) ↓ m.call_args == ((a, b), {'x': 1})
3: 依存先の挙動を定義する
テスト対象 mock テスト
return_value で戻り値定義
>>> m.return_value = 999 >>> m() 999
urlopen().read()
def foo(name): f = urlopen('http://%s.com/' % name) raw = f.read() d = json.loads(raw) return d['data']
return_value で戻り値定義
>>> m.return_value.meth.return_value = 1 >>> m().meth() 1
@patch('urllib.request.urlopen') def test_foo(m): m.return_value.read.return_value = '…' result = foo('wozozo') assert m.call_count == 1 assert (m.call_args == (('http://…',), {})) assert result == '…'
複雑な戻り値は side_effect
>>> def f(x, y): ... return x + y ... >>> m.side_effect = f >>> m(1, 2) 3
side_effect に例外を指定
>>> m.side_effect = TypeError >>> m() Traceback (most recent call last): ... TypeError
テスト対象 mock テスト
mock でユニットテストしやすくなる
より詳しい話
• 公式ドキュメント • voluntas 「requests と mock を使ってみる」
hDp://voluntas.hatenablog.com/entry/20111124/1322069748
• ぁっぉ 「mock はこう使え」 hDp://d.hatena.ne.jp/atsuoishimoto/20120310/1331311730