[Python] come fare il mocking di urlopen
Pietro
peter.zamb a gmail.com
Ven 18 Ago 2017 11:07:58 CEST
Ciao,
sto cercando di capire come fare il mock di urlopen, in modo da
testare il comportamento di una funzione, vorrei capire:
cosa sto sbagliando
come rendere il codice testabile sia su python3 che su python2
il codice è il seguente:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import tempfile
import os
try:
# Python 3.x
from unittest import mock
from urllib.request import urlopen
from urllib.error import URLError
URLOPEN_MOCKPATH = 'urllib.request.urlopen'
except ImportError:
# Python 2.x
import mock
from urllib2 import urlopen
from urllib2 import URLError
URLOPEN_MOCKPATH = 'urllib2.urlopen'
import pytest
from pytest import raises
def myfunc(url, filename):
try:
chkret = urlopen(url)
except URLError:
chkret = ""
if chkret:
result = chkret.read()
with open(filename, mode='w') as wfile:
wfile.write(result)
else:
with open(filename, mode='r') as rfile:
result = rfile.read()
return result
@mock.patch(URLOPEN_MOCKPATH, autospec=True)
def test_myfunc(mock_urlopen):
# create a temp file
tmp_dir = tempfile.mkdtemp()
filename = os.path.join(tmp_dir, 'mycache.txt')
with open(filename, mode='w') as w:
w.write("Success! (from file)")
# ===========================================
mock_chkret = mock.Mock()
mock_chkret.read.return_value = "Success! (from urlopen)"
mock_urlopen.return_value = mock_chkret
result = myfunc("http://myulr.it", filename)
mock_urlopen.assert_called()
mock_chkret.read.assert_called()
assert result == "Success! (from urlopen)"
# ===========================================
mock_urlopen.return_value = URLError
result = myfunc("http://myulr.it", filename)
mock_urlopen.assert_called()
assert result == "Success! (from file)"
# remove temporary file and directory
os.rm(filename)
os.rmdir(tmp_dir)
Quando eseguo i test ottengo:
$ pytest tests/test_urlopen_mock.py
======================= test session starts =================================
platform linux -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /home/pietro/docdat/synapsees/src/synapsees-raspberry-git, inifile:
collected 1 item
tests/test_urlopen_mock.py F
======================= FAILURES ======================================
__________________________ test_myfunc
_________________________________________
mock_urlopen = <function urlopen at 0x7ff54690c0d0>
@mock.patch(URLOPEN_MOCKPATH, autospec=True)
def test_myfunc(mock_urlopen):
# create a temp file
tmp_dir = tempfile.mkdtemp()
filename = os.path.join(tmp_dir, 'mycache.txt')
with open(filename, mode='w') as w:
w.write("Success! (from file)")
# ===========================================
mock_chkret = mock.Mock()
mock_chkret.read.return_value = "Success! (from urlopen)"
mock_urlopen.return_value = mock_chkret
result = myfunc("http://myulr.it", filename)
> mock_urlopen.assert_called()
tests/test_urlopen_mock.py:55:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.6/unittest/mock.py:197: in assert_called
return mock.assert_called(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <MagicMock name='urlopen' spec='function' id='140691438470928'>
def assert_called(_mock_self):
"""assert that the mock was called at least once
"""
self = _mock_self
if self.call_count == 0:
msg = ("Expected '%s' to have been called." %
self._mock_name or 'mock')
> raise AssertionError(msg)
E AssertionError: Expected 'urlopen' to have been called.
/usr/lib/python3.6/unittest/mock.py:786: AssertionError
==================== 1 failed in 0.13 seconds ===================
In sostanza sta ignorando il mio mock, e continua ad usare la funzione
urlopen, infatti se commento le righe:
mock_urlopen.assert_called()
mock_chkret.read.assert_called()
pytest fallisce perché restituisce il valore dal file e non da urlopen:
$ pytest tests/test_urlopen_mock.py
==================== test session starts ============================
platform linux -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /home/pietro/docdat/synapsees/src/synapsees-raspberry-git, inifile:
collected 1 item
tests/test_urlopen_mock.py F
========================= FAILURES =============================
____________________________ test_myfunc ________________________________
mock_urlopen = <function urlopen at 0x7f958ce380d0>
@mock.patch(URLOPEN_MOCKPATH, autospec=True)
def test_myfunc(mock_urlopen):
# create a temp file
tmp_dir = tempfile.mkdtemp()
filename = os.path.join(tmp_dir, 'mycache.txt')
with open(filename, mode='w') as w:
w.write("Success! (from file)")
# ===========================================
mock_chkret = mock.Mock()
mock_chkret.read.return_value = "Success! (from urlopen)"
mock_urlopen.return_value = mock_chkret
result = myfunc("http://myulr.it", filename)
#mock_urlopen.assert_called()
#mock_chkret.read.assert_called()
> assert result == "Success! (from urlopen)"
E AssertionError: assert 'Success! (from file)' == 'Success!
(from urlopen)'
E - Success! (from file)
E ? ^^
E + Success! (from urlopen)
E ? ^^ ++ +
tests/test_urlopen_mock.py:58: AssertionError
================== 1 failed in 0.06 seconds ========================
Cosa sto sbagliando? come fare a far si che usi mock_urlopen?
Al momento mi interessa far funzionare i test solo su python3, ma non
sarebbe male riuscire a farli girare anche sotto python2, voi come
fate di solito?
Suggerimenti?
Grazie
Pietro
Maggiori informazioni sulla lista
Python