Pre*_*zel 11 python unit-testing dependency-injection mocking
Python对我来说是一种相对较新的语言.单元测试和依赖注入是我现在已经做了一段时间的事情,所以我从C#的角度来熟悉它.
最近,我写了这段Python代码:
import requests # my dependency: http://docs.python-requests.org/en/latest/
class someClass:
def __init__(self):
pass
def __do(self, url, datagram):
return requests.post(self, url, datagram)
Run Code Online (Sandbox Code Playgroud)
然后我意识到我刚刚创建了一个硬编码的依赖项.的Bleh.
我曾考虑改变我的代码来做"构造函数"依赖注入:
def __init__(self,requestLib=requests):
self.__request = requestLib
def __do(self, url, datagram):
return self.__request.post(self, url, datagram)
Run Code Online (Sandbox Code Playgroud)
这现在允许我为单元测试注入假/模拟依赖,但不确定这是否被认为是Python-ic.所以我向Python社区寻求指导.
有哪些Python-ic方法可以做基本的DI(主要是为了编写利用Mocks/Fakes的单元测试)?
ADDENDUM对于对模拟答案感到好奇的人,我决定在这里问一个单独的问题:@ mock.patch如何知道每个模拟对象使用哪个参数?
不要那样做.只需正常导入请求并正常使用它们.将库作为参数传递给构造函数是一件有趣的事情,但对于您的目的而言,并不是非常pythonic和不必要的.要在单元测试中模拟事物,请使用模拟库.在python 3中,它内置于标准库中
https://docs.python.org/3.4/library/unittest.mock.html
在python 2中,您需要单独安装它
https://pypi.python.org/pypi/mock
你的测试代码看起来像这样(使用python 3版本)
from unittest import TestCase
from unittest.mock import patch
class MyTest(TestCase):
@patch("mymodule.requests.post")
def test_my_code(self, mock_post):
# ... do my thing here...
Run Code Online (Sandbox Code Playgroud)
虽然注入 requests 模块可能有点太多,但将一些依赖项作为可注入性是一个很好的做法。
多年使用没有任何 DI 自动装配框架的 Python 和带有 Spring 的 Java 之后,我开始意识到简单的 Python 代码通常不需要使用自动装配的依赖注入框架(自动装配是 Guice 和 Spring 在 Java 中所做的),即,只是做这样的事情可能就足够了:
def foo(dep = None): # great for unit testing!
...
Run Code Online (Sandbox Code Playgroud)
这是纯粹的依赖注入(非常简单),但没有为您自动注入它们的神奇框架。调用者必须实例化依赖项,或者您可以这样做:
def __init__(self, dep = None):
self.dep = dep or Dep()
Run Code Online (Sandbox Code Playgroud)
但是,当您寻求更大的应用程序时,这种方法不会削减它。为此,我提出了一个可注入的微框架,它不会让人感觉非 Pythonic,但会提供一流的依赖注入自动装配。
在人类依赖注入™的座右铭下,它是这样的:
# some_service.py
class SomeService:
@autowired
def __init__(
self,
database: Autowired(Database),
message_brokers: Autowired(List[Broker]),
):
pending = database.retrieve_pending_messages()
for broker in message_brokers:
broker.send_pending(pending)
Run Code Online (Sandbox Code Playgroud)
# database.py
@injectable
class Database:
...
Run Code Online (Sandbox Code Playgroud)
# message_broker.py
class MessageBroker(ABC):
def send_pending(messages):
...
Run Code Online (Sandbox Code Playgroud)
# kafka_producer.py
@injectable
class KafkaProducer(MessageBroker):
...
Run Code Online (Sandbox Code Playgroud)
# sqs_producer.py
@injectable
class SQSProducer(MessageBroker):
...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6007 次 |
| 最近记录: |