Уведомления

Группа в Telegram: @pythonsu

#1 Июнь 9, 2012 14:10:22

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

У меня наблюдается категорическое непонимание технологии работы с моками, помогите, пожалуйста!

Имеется вот такой код:

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.



Офлайн

#2 Июнь 9, 2012 15:20:14

bw
От:
Зарегистрирован: 2007-09-26
Сообщения: 938
Репутация: +  20  -
Профиль   Адрес электронной почты  

Как готовить mock

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

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

..bw



Офлайн

#3 Июнь 9, 2012 16:02:44

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

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

PS: А, понял. Вы предлагаете гнуть код под тесты. Это порочный подход, по-моему.



Отредактировано Ed (Июнь 9, 2012 16:04:24)

Офлайн

#4 Июнь 9, 2012 16:24:12

bw
От:
Зарегистрирован: 2007-09-26
Сообщения: 938
Репутация: +  20  -
Профиль   Адрес электронной почты  

Как готовить mock

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

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

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

..bw



Офлайн

#5 Июнь 9, 2012 16:30:36

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

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

Но все равно, не удержусь:
Честно говоря за код такого вида:

from transmissionrc import client as Transmission
from netrc import netrc

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

По-моему моки как раз для таких случаев и нужны. Чтобы можно было сэмулировать поведение неких внешних объектов, нет разве?



Отредактировано Ed (Июнь 9, 2012 16:32:14)

Офлайн

#6 Июнь 9, 2012 16:41:59

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

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



Офлайн

#7 Июнь 9, 2012 21:23:25

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

Как готовить mock

Пример, по-моему, неудачный. Там вроде нечего тестировать.

Может как-то так?

#!/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()

Офлайн

#8 Июнь 9, 2012 21:49:05

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

О, это уже интереснее, спасибо. А как сделать, чтобы работало

torr.fields['name'][:30]
? Просто MagicMock() думаю такого не сделает, его нужно научить как-то.



Отредактировано Ed (Июнь 9, 2012 21:50:27)

Офлайн

#9 Июнь 9, 2012 22:17:08

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

Как готовить mock

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?'
т.к. непонятно откуда эта функция.

Отредактировано reclosedev (Июнь 9, 2012 22:18:08)

Офлайн

#10 Июнь 9, 2012 22:46:32

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как готовить mock

Нет, это я стормозил. Теперь такой вопрос. Тоже самое я делаю без всякого 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. В чем тогда смысл его использования?



Отредактировано Ed (Июнь 9, 2012 22:55:24)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version