У меня есть код, который выглядит так:
import xmlrpclib
class Base(object):
def __init__(self, server):
self.conn = xmlrpclib.ServerProxy(server)
def foo(self):
return self.conn.do_something()
class Derived(Base):
def foo(self):
if Base.foo():
return self.conn.do_something_else()
Как использовать имитацию для проверки поведения класса Derived? Я не хочу предполагать, что все, о чем говорит соединение XML-RPC, действительно существует, но я чувствую, что издевательство над модулем xmlrpclib требует слишком больших знаний о реализации класса Base (для которого у меня есть другие тесты).
Или, я думаю, мне даже использовать насмешку, чтобы проверить это? Если нет, как бы вы это протестировали?






Вы можете создать поддельный класс ServerProxy и заменить его для тестирования.
Что-то вроде этого:
class FakeServerProxy(object):
def __init__(self, server):
self.server = server
def do_something(self):
pass
def do_something_else(self):
pass
def test_derived():
xmlrpclib.ServerProxy = FakeServerProxy
derived = Derived(None)
derived.foo()
Derived тесно связан с Base, так что вам придется копаться во внутренностях Base (не прибегая к вуду). Как насчет того, чтобы сделать Base членом, а не предком, и подделать Base?
Я предполагаю, что это работает в Python из-за утиной печати, но это не очень неудовлетворительное решение для моей стороны C++, поскольку оно не допускает принцип подстановки.
@Zach Hirsch: все тестирование подкласса также является тестом суперкласса. Вы не можете относиться к Derived как к не имеющему отношения к Base. Ваши тесты для Derived будут либо подклассом ваших тестов для Base, либо клонами с копированием и вставкой.
С помощью некоторого тривиального рефакторинга (вызов do_something_else извлечен) вы можете протестировать логику Derived.foo без необходимости «знать» о XMLRPC.
import xmlrpclib
class Base(object):
def __init__(self, server):
self.conn = xmlrpclib.ServerProxy(server)
def foo(self):
return self.conn.do_something()
class Derived(Base):
def foo(self):
if Base.foo(self):
return self._bar()
def _bar(self):
# moved to its own method so that you
# you can stub it out to avoid any XMLRPCs
# actually taking place.
return self.conn.do_something_else()
import mox
d = Derived('http://deep-thought/unanswered/tagged/life+universe+everything')
m = mox.Mox()
m.StubOutWithMock(Base, 'foo')
m.StubOutWithMock(d, '_bar')
Base.foo(d).AndReturn(True)
d._bar() # Will be called becase Boo.foo returns True.
m.ReplayAll()
d.foo()
m.UnsetStubs()
m.VerifyAll()
Альтернативно или даже предпочтительно вы можете извлечь вызов do_something_else в метод bar на Base. Что имеет смысл, если мы согласны с тем, что Base инкапсулирует все ваши действия XMLRPC.
В примере используется насмешливая библиотека пимокс, но суть ее остается неизменной независимо от вашего стиля насмешки.
Но тогда мне нужно знать, что класс
Baseиспользует xmlrpclib. Я хотел бы попытаться избежать этого, поскольку кажется неправильным, что при изменении реализации базового класса мне придется изменить тесты для производного класса, но не фактическую реализацию производного класса.