【问题标题】:How to handle importing when testing Flask apps: AssertionError: View function mapping is overwriting an existing endpoint function测试 Flask 应用程序时如何处理导入:AssertionError: View function mapping is overwriting an existing endpoint function
【发布时间】:2019-02-08 19:46:33
【问题描述】:

我正在努力理解如何在 Flask 中编写测试。

我继承了一个应用程序,该应用程序已经进行了一系列测试,这些测试命中了 /login 之类的路由,并测试了响应是否符合预期。

我的情况要复杂得多。我需要测试一个路由/方法,根据具体情况,点击一个外部 api,确定应用程序本身运行的容器中是否存在路径,启动一个需要 10 多分钟才能在另一台机器上运行的进程 - - 各种各样的事情。所以我不能只打路线,看看我是否得到了我想要的;我需要模拟和修补来模拟各种外部世界状态的影响。

现在我在brain_db/views.py 中有这样定义的路由:

@app.route('/label_view/<int:scan_number>')
@login_required
def label_view(scan_number):
    <so much complicated logic>

在同一个文件brain_db/views.py中定义的第一条路由是

@app.route('/surface_test')
def surface_test():
    <some code>

这是引发错误的文件的简化版本:

import unittest

from mock import MagicMock, patch
from flask_brain_db.test_helpers import set_up, tear_down
from flask_brain_db.brain_db.models import Scan
from brain_db.views import label_view

class BrainDBTest(unittest.TestCase):

    def setUp(self):
        app, db = set_up()

        scan = Scan(1, '000001', '000001_MR1', 'scan.nii.gz', scan_number=1)
        db.session.add(scan)
        scan = Scan.query.filter(Scan.scan_number == 1).first()
        db.session.commit()

    def tearDown(self):
        tear_down()

    def mock_volume_views_setup(self)

        scan = Scan.query.filter(Scan.scan_number == 1).first()
        container_file_path = '/path/to/file/in/container'
        return scan, container_file_path

    def mock_os_path_exists(self, arg):
        return True

    @patch('brain_db_helpers.volume_views_setup', mock_volume_views_setup)
    @patch('os.path.exists', mock_os_path_exists)
    def test_label_view(self):
        rv = label_view(1)
        assert(True) # I'll actually write tests when I figure out that I can!
        print rv

这是错误:

======================================================================
ERROR: brain_db.tests.test (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: brain_db.tests.test
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/unittest/loader.py", line 254, in _find_tests
    module = self._get_module_from_name(name)
  File "/usr/local/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
    __import__(name)
  File "/usr/src/app/flask_brain_db/brain_db/tests/test.py", line 7, in <module>
    from brain_db.views import label_view
  File "/usr/src/app/flask_brain_db/brain_db/views.py", line 36, in <module>
    @app.route('/surface_test')
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1250, in decorator
    self.add_url_rule(rule, endpoint, f, **options)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 66, in wrapper_func
    return f(self, *args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1221, in add_url_rule
    'existing endpoint function: %s' % endpoint)
AssertionError: View function mapping is overwriting an existing endpoint function: surface_test

我为解决我的问题所做的工作:我已经阅读了 SO 上引用相同 AssertionError 的一堆帖子。例如。 12。我可以看到问题的一般形式是我的路线已经定义,并且

from brain_db.views import label_view

再次执行views 模块,因此重新定义了路由,所以我得到了一个错误。

我不明白我应该如何避免这种情况。我需要能够将方法导入另一个文件才能对其进行测试。是否所有路由都应该包含在if __name__ == main 中?我是 Flask 开发的新手,还没有看到这种情况的示例代码;我怀疑这是正确的解决方案;当您尝试搜索阻止代码在导入时执行时,它只是提供的唯一内容。

我现在运行测试的方式是通过应用程序顶层的文件manage.py。它包含以下方法:

@manager.command
def test():
    """Runs the tests without coverage"""
    tests = unittest.TestLoader().discover(start_dir='.', pattern='test*.py')
    res = unittest.TextTestRunner(verbosity=2).run(tests)
    sys.exit(not res.wasSuccessful())

我在命令行运行python manage.py test

这也可能是相关的,虽然我将失败的测试放在 Brain_db 中的子模块中,但在它之前运行了几个测试,这些测试命中了应用程序中定义的路由并测试了预期的结果。但是,注释掉这些测试对我的测试失败的方式没有影响。

最后,我最初在from flask_brain_db.brain_db.models import Scan 行遇到错误:

ERROR: brain_db.tests.test (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: brain_db.tests.test
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/unittest/loader.py", line 254, in _find_tests
    module = self._get_module_from_name(name)
  File "/usr/local/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
    __import__(name)
  File "/usr/src/app/flask_brain_db/brain_db/tests/test.py", line 5, in <module>
    from flask_brain_db.brain_db.models import Scan
  File "/usr/src/app/flask_brain_db/brain_db/models.py", line 6, in <module>
    class Scan(db.Model):
  File "/usr/local/lib/python2.7/site-packages/flask_sqlalchemy/model.py", line 67, in __init__
    super(NameMetaMixin, cls).__init__(name, bases, d)
  File "/usr/local/lib/python2.7/site-packages/flask_sqlalchemy/model.py", line 121, in __init__
    super(BindMetaMixin, cls).__init__(name, bases, d)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 65, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 116, in _as_declarative
    _MapperConfig.setup_mapping(cls, classname, dict_)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 144, in setup_mapping
    cfg_cls(cls_, classname, dict_)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 172, in __init__
    self._setup_table()
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 465, in _setup_table
    **table_kw)
  File "/usr/local/lib/python2.7/site-packages/flask_sqlalchemy/model.py", line 90, in __table_cls__
    return sa.Table(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/sql/schema.py", line 439, in __new__
    "existing Table object." % key)
InvalidRequestError: Table 'scan' is already defined for this MetaData instance.  Specify 'extend_existing=True' to redefine options and columns on an existing Table object.

我让它消失了,包括

__table_args__ = {'extend_existing': True}

在模型定义中,但我不知道我是否应该这样做,我怀疑我只是推迟了我现在遇到的同样问题。似乎根本问题是我不知道如何在不重新定义一堆已经定义的东西的情况下编写测试。

解决这个问题的正确方法是什么?如果我需要提供任何其他信息,请告诉我。

【问题讨论】:

    标签: python unit-testing flask python-import


    【解决方案1】:

    您应该能够从您的应用对象访问所有视图功能。尝试删除“from brain_db.views import label_view”行,并在运行 set_up() 后使用以下代码定义您的 label_view 方法:

    label_view = app.view_functions["label_view"]

    【讨论】:

    • 感谢您的回答。我还不能接受它,因为我无法评估它 - 删除 import 语句消除了该错误,但揭示了一些下游错误,这些错误阻止我自己查看 label_view 是否可以通过这种方式访问​​。
    猜你喜欢
    • 2016-04-24
    • 2020-06-18
    • 2022-11-20
    • 2015-06-19
    • 2019-04-08
    • 2020-11-29
    • 2017-09-22
    • 2019-05-04
    • 2019-07-01
    相关资源
    最近更新 更多