Flask 烧瓶和Pytest中的多个夹具和全局状态

Flask 烧瓶和Pytest中的多个夹具和全局状态,flask,pytest,fixtures,Flask,Pytest,Fixtures,我在Flask上构建的web应用程序具有事件通知机制,如果通过应用程序的API执行特定的REST操作,则可能会生成通知。直到昨天,我的测试覆盖率还存在差距 该机制基于一个通知程序类,该类为不同类型的通知方法(如Slack)的子类。通知配置是在应用程序初始化时从数据库加载的,但是通知机制不是有状态的,并且不会持久化数据,因此我创建了一个TestNotifier,它只在内存中的数组中维护通知列表。然后,我创建了一个fixture,经过大量的修改,我测试了通知机制 conftest.py: @pyte

我在Flask上构建的web应用程序具有事件通知机制,如果通过应用程序的API执行特定的REST操作,则可能会生成通知。直到昨天,我的测试覆盖率还存在差距

该机制基于一个通知程序类,该类为不同类型的通知方法(如Slack)的子类。通知配置是在应用程序初始化时从数据库加载的,但是通知机制不是有状态的,并且不会持久化数据,因此我创建了一个TestNotifier,它只在内存中的数组中维护通知列表。然后,我创建了一个fixture,经过大量的修改,我测试了通知机制

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
,但是,尽管它可以用于测试,但它与其他代码不一致,并且肯定会以错误的方式困扰我。再一次让我怀疑我是否完全走错了路

我可以在数据库中使用一个临时文件或临时表来存储通知,但我宁愿保留内存