Flask 烧瓶和Pytest中的多个夹具和全局状态
我在Flask上构建的web应用程序具有事件通知机制,如果通过应用程序的API执行特定的REST操作,则可能会生成通知。直到昨天,我的测试覆盖率还存在差距 该机制基于一个通知程序类,该类为不同类型的通知方法(如Slack)的子类。通知配置是在应用程序初始化时从数据库加载的,但是通知机制不是有状态的,并且不会持久化数据,因此我创建了一个TestNotifier,它只在内存中的数组中维护通知列表。然后,我创建了一个fixture,经过大量的修改,我测试了通知机制Flask 烧瓶和Pytest中的多个夹具和全局状态,flask,pytest,fixtures,Flask,Pytest,Fixtures,我在Flask上构建的web应用程序具有事件通知机制,如果通过应用程序的API执行特定的REST操作,则可能会生成通知。直到昨天,我的测试覆盖率还存在差距 该机制基于一个通知程序类,该类为不同类型的通知方法(如Slack)的子类。通知配置是在应用程序初始化时从数据库加载的,但是通知机制不是有状态的,并且不会持久化数据,因此我创建了一个TestNotifier,它只在内存中的数组中维护通知列表。然后,我创建了一个fixture,经过大量的修改,我测试了通知机制 conftest.py: @pyte
conftest.py
:
@pytest.fixture(scope='class')
def client(seeded_app):
return seeded_app.test_client()
class TestNotifier(Notifier):
def _config(self, config):
self._notifications = []
self._config = config
def notify(self, message):
self._notifications.append(message)
@property
def notifications(self):
return self._notifications
register_notifier('test', TestNotifier)
@pytest.fixture
def notifier(app):
with app.app_context():
return get_notifiers()[0]
notifiers = {}
notifiers_inited = None
def register_notifier(type, cls):
notifiers[type] = cls
def get_notifiers():
# TODO: why can't I use g.notifiers for this?
#if 'notifiers' not in g:
# g.notifiers = ...
#return g.notifiers
# pylint: disable=global-statement
global notifiers_inited
if not notifiers_inited:
db = get_db()
res = db.execute('select * from notifiers').fetchall()
if not res:
return None
notifiers_inited = [
notifiers[row['type']](row['name'], json.loads(row['config']))
for row in res
]
return notifiers_inited
在notifier.py
中:
@pytest.fixture(scope='class')
def client(seeded_app):
return seeded_app.test_client()
class TestNotifier(Notifier):
def _config(self, config):
self._notifications = []
self._config = config
def notify(self, message):
self._notifications.append(message)
@property
def notifications(self):
return self._notifications
register_notifier('test', TestNotifier)
@pytest.fixture
def notifier(app):
with app.app_context():
return get_notifiers()[0]
notifiers = {}
notifiers_inited = None
def register_notifier(type, cls):
notifiers[type] = cls
def get_notifiers():
# TODO: why can't I use g.notifiers for this?
#if 'notifiers' not in g:
# g.notifiers = ...
#return g.notifiers
# pylint: disable=global-statement
global notifiers_inited
if not notifiers_inited:
db = get_db()
res = db.execute('select * from notifiers').fetchall()
if not res:
return None
notifiers_inited = [
notifiers[row['type']](row['name'], json.loads(row['config']))
for row in res
]
return notifiers_inited
最后,测试用例依赖于两个夹具:
def test_post_thingy(client, notifier):
...
第一个问题是这样做是否正确。我不确定我是否正确或有效地使用了固定装置。我找不到依赖多个固定装置的有用例子,至少不是在烧瓶中
第二个问题是关于全局变量的使用。最初,我遵循代码中其他地方使用的模式,并基于Flask的教程,使用存储在g
中的单例。这用于get_db()
,get_log()
,等等:如果db connection不在g中,则创建一个并将其添加到g中。在任何情况下,都应以g的形式返回数据库连接。问题是,这不适用于通知测试。如果我跟踪TestNotifier的实例化,那么fixture会使用一个独立于实际测试的会话——因此,当测试运行时,服务器端会使用一个独立于fixture的TestNotifier对象,我无法查看之后“发送”的通知
从代码中可以看出,解决这一问题的方法是使用一个直接向上的全局变量,而不是Flask的g
,但是,尽管它可以用于测试,但它与其他代码不一致,并且肯定会以错误的方式困扰我。再一次让我怀疑我是否完全走错了路
我可以在数据库中使用一个临时文件或临时表来存储通知,但我宁愿保留内存