Найти - Пользователи
Полная версия: Как готовить mock
Начало » Python для новичков » Как готовить mock
1 2
Ed
У меня наблюдается категорическое непонимание технологии работы с моками, помогите, пожалуйста!

Имеется вот такой код:
from netrc import netrc
from transmissionrpc import Client as Transmission

def list_torrents(trans, _args):
"""List torrents."""
for num, torr in trans.list().iteritems():
fields = torr.fields
print num, fields['name'][:30], status(torr), torr.progress, torr.eta

def main():
nrc = netrc()
tr_login, _, tr_passwd = nrc.authenticators('localhost')
trans = Transmission(user=tr_login, password=tr_passwd, timeout=10)
list_torrents(trans, [])

Я хочу его протестировать. Естественно поднимать для этого transmission-daemon и прописывать пароли в ~/.netrc я не имею ни малейшего желания. Стало быть нужно это делать через моки. Документацию по mock http://www.voidspace.org.uk/python/mock/ читал, но когда доходит до реальных тестов получается какая-то фигня - слишком детализировано, зависит от реализации и очень многословно.

А хочется-то простого - сделать моки для Transmission, netrc и для вывода, запустить main() поймать вывод и сравнить его с тем, чего ожидаешь. Помогите, кто чем может! Желательно кодом :)
Да, в качестве тест-фреймворка используется nose.

bw
#from netrc import netrc
#from transmissionrpc import Client as Transmission
def list_torrents(trans, _args):
    # ...
def main(netrc, Transmission):
    # ...
Теперь тестируй :-).
Не для этого Mock'и придуманы, точнее прекрасно можно без них обойтись.

p.s. А вообще, мне больше Fudge нравится.

..bw
Ed
Это о чем было? Можно поподробнее? Как я могу без них обойтись, если код, который я хочу тестировать использует transmissionrc и netrc?

PS: А, понял. Вы предлагаете гнуть код под тесты. Это порочный подход, по-моему.
bw
Ed> Как я могу без них обойтись, если код, который я хочу тестировать использует transmissionrc и netrc?
Ну так мы же тестируем не netrc и transmissionrpc, зачем их приплетать. Обойтись без реальных реализаций можно передав тестируемому `main` свои dump'ы, с заранее известным поведением.

Ed> Вы предлагаете гнуть код под тесты. Это порочный подход, по-моему.
Отнюдь. Конечно, везде нужно знать меру и я ведь не предлагаю полностью в TDD уходить, хотя разработка через тестирование, например, позволят написать лучший API (мне позволяет :-) чем наколеночное проектирование.

А вот mock'и нужны далеко не всегда и скорее их использовании порочно. Я тоже какое-то время увлекся ими, но вовремя опомнился, теперь только для hook'ов использую при тестировании старого кода.

..bw
Ed
Я не хочу спорить, я хочу получить совет. Скажем так - условие у меня такое, что код я трогать не могу. Мне тесты для него нужно написать.

Но все равно, не удержусь:
Честно говоря за код такого вида:
from transmissionrc import client as Transmission
from netrc import netrc

def main(netrc, Transmission):
....
я бы стрелял из рогатки. Передавать модули в main, чтобы тестирование заработало. Если это не изгибание кода под тесты, то что тогда?

По-моему моки как раз для таких случаев и нужны. Чтобы можно было сэмулировать поведение неких внешних объектов, нет разве?
Ed
bw
p.s. А вообще, мне больше Fudge нравится…
Я выбрал mock, кроме всего прочего еще и потому что его автор - это автор unittest из стандартной библиотеки и mock туда же скоро попадет, если не попал уже. Я не люблю внешние зависимости, предпочитаю стандартные средства.
reclosedev
Пример, по-моему, неудачный. Там вроде нечего тестировать.

Может как-то так?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mock
from StringIO import StringIO
netrc = mock.Mock()
trans = mock.Mock()
 
# сюда нужно будет вписать все торренты?
# пока, просто моки
desired_items = {i: mock.MagicMock() for i in range(10)}
 
with mock.patch.dict('sys.modules',
                     {'transmissionrpc': trans,
                      'netrc': netrc}):
    netrc.netrc.return_value.authenticators.return_value = ['user', '?', 'password']
    trans.Client.return_value.list.return_value = desired_items
 
    with mock.patch('sys.stdout', new_callable=StringIO) as stdout:
        import some_lib
        some_lib.main()
 
    # assert 'desired output' in stdout.getvalue() 
    print stdout.getvalue()
Ed
О, это уже интереснее, спасибо. А как сделать, чтобы работало
torr.fields['name'][:30]
? Просто MagicMock() думаю такого не сделает, его нужно научить как-то.
reclosedev
Ed
О, это уже интереснее, спасибо. А как сделать, чтобы работало
torr.fields['name'][:30]
? Просто MagicMock() думаю такого не сделает, его нужно научить как-то.
Так работает, сохраненный вывод:
0 <MagicMock name='mock.fields.__getitem__().__getitem__()' id='26179088'> status? <MagicMock name='mock.progress' id='26473104'> <MagicMock name='mock.eta' id='26473264'>
1 <MagicMock name='mock.fields.__getitem__().__getitem__()' id='26578192'> status? <MagicMock name='mock.progress' id='26603408'> <MagicMock name='mock.eta' id='26624080'>
2 <MagicMock name='mock.fields.__getitem__().__getitem__()' id='26720816'> status? <MagicMock name='mock.progress' id='27098288'> <MagicMock name='mock.eta' id='27098448'>
3 <MagicMock name='mock.fields.__getitem__().__getitem__()' id='27215696'> status? <MagicMock name='mock.progress' id='27240912'> <MagicMock name='mock.eta' id='27241072'>
...
т.е. fields['name'] вызывает __getitem__ и возвращает magic_mock, также и magic_mock[:30] возвращает мок.
Или я неправильно понял вопрос?

P.S.
“status?” это
def status(x):
    return 'status?'
т.к. непонятно откуда эта функция.
Ed
Нет, это я стормозил. Теперь такой вопрос. Тоже самое я делаю без всякого mock-а вот таким кодом:
class FakeTransmission(object):
def __init__(self, user, password, timeout=10):
pass

def list(self):
return {1: transmissionrpc.Torrent(self,
{'id': 1, 'name': 'My torrent', 'status': 0,
'sizeWhenDone': 100, 'leftUntilDone': 50,
'eta': 20})}

class FakeNetrc(object):
def authenticators(self, _host):
return 1, 2, 3

def test_ls():
mod.Transmission = FakeTransmission
mod.netrc = FakeNetrc
mod.sys.stdout = StringIO()
mod.main()
mod.sys.stdout.seek(0)
assert mod.sys.stdout.read() == "1 My torrent stopped 50.0 0:00:20\n"

Мне он кажется более понятным, простым и управляемым. И писать так было намного легче. Если что-то ломалось, то в трэйсбэке было видно где, в отличие от моих попыток сделать это с использованием mock. В чем тогда смысл его использования?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB