LoginSignup
4
4

More than 5 years have passed since last update.

urllib2をMock化してunittestする

Posted at

urllib2.urlopen()しているやつをMock化してテストを書いたときに若干はまったので備忘録として。

まずはやってみる

テスト対象コード

実際はいろいろやっているコードですが、シンプルにするためにとりあえず以下のような感じに。

target.py
import urllib2

def get(url):
    return urllib2.urlopen().read()

悪い例

安易に最初に書いたコードがこれです。一件すると問題なさそうですが、これを実行すると1回目のアサーションは通りますが、2回目のアサーションでAssertionError: 'hoge' != ''となりエラーになります。

bad_test_sample.py
from nose.tools import *
from mock import Mock
from StringIO import StringIO
import urllib2
import target

def test_get():
    urllib2.urlopen = Mock(return_value=StringIO('hoge'))
    eq_('hoge', target.get("http://dummy-url"))
    eq_('hoge', target.get("http://dummy-url"))

1回目ならまだしも、2回目以降でエラーになりますので???となります。実際のテストはこのように二度実行はしないとでしょうから、明らかにおかしいタイミングでエラーになって見えます。少なくとも自分の時はそうでした。

正しい例

結論から言うと「readが必要->StringIOで作る」が間違っているので、それをしないようにして、そこもMockで済ませます。

good_test_sample.py
from nose.tools import *
from mock import Mock
import urllib2
import target

def test_get():
    urllib2.urlopen = Mock()
    urllib2.urlopen.return_value.read.return_value = 'hoge'
    eq_('hoge', target.get("http://dummy-url"))
    eq_('hoge', target.get("http://dummy-url"))

解説

StringIOで渡した場合、確かにちゃんとしたread()メソッドがついてきますが、逆にちゃんとしているが故に二度目以降の実行でも同一ストリームを参照し、さらにポインタが既に最後尾にあります。なので、二度目以降はread()しても何も得られません。やっぱりポインタ周りはプログラマ的には必修だね。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4