Дразнить urllib2.urlopen и lxml.etree.parse с помощью pymox - PullRequest
2 голосов
/ 30 июня 2010

Я пытаюсь протестировать некоторый код Python, который использует urllib2 и lxml.

Я видел несколько постов в блоге и переполнение стека, где люди хотят тестировать исключения, с помощью urllib2.Я не видел примеров тестирования успешных вызовов.

Я иду по правильному пути?

У кого-нибудь есть предложения, как заставить это работать?

Вот чтоУ меня так далеко:

import mox
import urllib
import urllib2
import socket
from lxml import etree

# set up the test
m = mox.Mox()
response = m.CreateMock(urllib.addinfourl)
response.fp = m.CreateMock(socket._fileobject)
response.name = None # Needed because the file name is checked.
response.fp.read().AndReturn("""<?xml version="1.0" encoding="utf-8"?>
<foo>bar</foo>""")
response.geturl().AndReturn("http://rss.slashdot.org/Slashdot/slashdot")
response.read = response.fp.read # Needed since __init__ is not called on addinfourl.
m.StubOutWithMock(urllib2, 'urlopen')
urllib2.urlopen(mox.IgnoreArg(), timeout=10).AndReturn(response)
m.ReplayAll()

# code under test
response2 = urllib2.urlopen("http://rss.slashdot.org/Slashdot/slashdot", timeout=10)
# Note: response2.fp.read() and response2.read() do not behave the same, as defined above.
# In [21]: response2.fp.read()
# Out[21]: '<?xml version="1.0" encoding="utf-8"?>\n<foo>bar</foo>'
# In [22]: response2.read()
# Out[22]: <mox.MockMethod object at 0x97f326c>
xcontent = etree.parse(response2)

# verify test
m.VerifyAll()

Не удается с:

Traceback (most recent call last):
  File "/home/jon/mox_question.py", line 22, in <module>
    xcontent = etree.parse(response2)
  File "lxml.etree.pyx", line 2583, in lxml.etree.parse (src/lxml/lxml.etree.c:25057)
  File "parser.pxi", line 1487, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:63708)
  File "parser.pxi", line 1517, in lxml.etree._parseFilelikeDocument (src/lxml/lxml.etree.c:63999)
  File "parser.pxi", line 1400, in lxml.etree._parseDocFromFilelike (src/lxml/lxml.etree.c:62985)
  File "parser.pxi", line 990, in lxml.etree._BaseParser._parseDocFromFilelike (src/lxml/lxml.etree.c:60508)
  File "parser.pxi", line 542, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:56659)
  File "parser.pxi", line 624, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:57472)
  File "lxml.etree.pyx", line 235, in lxml.etree._ExceptionContext._raise_if_stored (src/lxml/lxml.etree.c:6222)
  File "parser.pxi", line 371, in lxml.etree.copyToBuffer (src/lxml/lxml.etree.c:55252)
TypeError: reading from file-like objects must return byte strings or unicode strings

Это потому, что response.read () не возвращает то, что я ожидал, чтобы вернуть.

Ответы [ 3 ]

4 голосов
/ 30 июня 2010

Я бы вообще не вникал во внутренности urllib2.Я думаю, это выходит за рамки того, о чем ты заботишься.Вот простой способ сделать это с помощью StringIO.Ключевым моментом здесь является то, что то, что вы намереваетесь анализировать как XML, просто должно быть подобным файлу с точки зрения типизации утки, это не обязательно должен быть фактический экземпляр addinfourl.

import StringIO
import mox
import urllib2
from lxml import etree

# set up the test
m = mox.Mox()
response = StringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
<foo>bar</foo>""")
m.StubOutWithMock(urllib2, 'urlopen')
urllib2.urlopen(mox.IgnoreArg(), timeout=10).AndReturn(response)
m.ReplayAll()

# code under test
response2 = urllib2.urlopen("http://rss.slashdot.org/Slashdot/slashdot", timeout=10)
xcontent = etree.parse(response2)

# verify test
m.VerifyAll()
2 голосов
/ 18 декабря 2011

Повторяя сказанное Питером, я бы добавил, что вам, возможно, не нужно беспокоиться о lxml внутренностях больше, чем о urllib2. Путем насмешки над lxml.etree вы можете полностью изолировать код, который вам действительно нужно протестировать, самостоятельно. Вот пример, который делает это, а также показывает, как вы можете использовать фиктивный объект для проверки вызова response.getcode ().

import mox
from lxml import etree
import urllib2

class TestRssDownload(mox.MoxTestBase):

    def test_rss_download(self):
        expected_response = self.mox.CreateMockAnything()
        self.mox.StubOutWithMock(urllib2, 'urlopen')
        self.mox.StubOutWithMock(etree, 'parse')
        self.mox.StubOutWithMock(etree, 'iterwalk')
        title_elem = self.mox.CreateMock(etree._Element)
        title_elem.text = 'some title'

        # Set expectations 
        urllib2.urlopen("http://rss.slashdot.org/Slashdot/slashdot", timeout=10).AndReturn(expected_response)
        expected_response.getcode().AndReturn(200)
        etree.parse(expected_response).AndReturn('some parsed content')
        etree.iterwalk('some parsed content', tag='{http://purl.org/rss/1.0/}title').AndReturn([('end', title_elem),])

        # Code under test
        self.mox.ReplayAll()
        self.production_code()

    def production_code(self):
        response = urllib2.urlopen("http://rss.slashdot.org/Slashdot/slashdot", timeout=10)
        response_code = response.getcode()
        if 200 != response_code:
            raise Exception('Houston, we have a problem ({0})'.format(response_code))
        tree = etree.parse(response)
        for ev, elem in etree.iterwalk(tree, tag='{http://purl.org/rss/1.0/}title'):
            # Do something with elem.text
            print('{0}: {1}'.format(ev, elem.text))
0 голосов
/ 30 июня 2010

Похоже, что ваша ошибка вообще не связана с mox - строка, вызывающая ошибку, читает из response2, который является прямым вызовом slashdot. Может быть, проверить этот объект и посмотреть, что это за содержимое?

РЕДАКТИРОВАТЬ: я не видел строку m.StubOutWithMock(urllib2, 'urlopen') выше, поэтому я подумал, что вы сравниваете два вызова; один издевался (ответ), а другой нет (ответ2). Обновленный ответ ниже.

...