如何在Flask测试代码中启动测试服务器?
Flask端到端测试中启动测试服务器的问题
运行Miguel Grinberg《Flask Web Development 2e》第15章的Selenium端到端测试时,控制台出现以下错误:
- Ignoring a call to 'app.run()' that would block the current 'flask' CLI command.
Only call 'app.run()' in an 'if name == "main"' guard.
随后浏览器显示“Firefox无法建立连接...”,测试服务器未成功启动。
测试代码(tests/test_selenium.py)
import threading import unittest from selenium import webdriver from app import create_app, db, fake from app.models import Role, User class SeleniumTestCase(unittest.TestCase): browser = None @classmethod def setUpClass(cls) -> None: try: cls.browser = webdriver.Firefox() except Exception as e: pass if cls.browser: cls.app = create_app('testing') cls.app_context = cls.app.app_context() cls.app_context.push() import logging logger = logging.getLogger('werkzeug') logger.setLevel('ERROR') db.create_all() Role.insert_roles() fake.users(10) fake.posts(10) admin_role = Role.query.filter_by(permissions=0xff).first() admin = User(email='john@example.com', password='cat', username='john', role=admin_role, confirmed=True) db.session.add(admin) db.session.commit() cls.server_thread = threading.Thread( target=cls.app.run, kwargs={ 'debug': 'false', 'use_reloader': False, 'use_debugger': False, 'host': '0.0.0.0', 'port': 5000 } ) cls.server_thread.start() @classmethod def tearDownClass(cls) -> None: if cls.browser: cls.browser.get('http://localhost:5000/shutdown') cls.browser.quit() cls.server_thread.join() db.drop_all() db.session.remove() cls.app_context.pop() def setUp(self) -> None: if not self.browser: self.skipTest('Web browser not available') def tearDown(self) -> None: pass def test_admin_home_page(self): self.browser.get('http://localhost:5000/') # fails here self.assertRegex(self.browser.page_source, 'Hello,\s+Stranger!') self.fail('Finish the test!')
补充说明
曾尝试使用Flask-Testing,但该库已无人维护。问题根源是显式调用app.run()与Flask CLI的隐式调用冲突,但不调用则测试服务器无法启动。希望通过现有Flask CLI的test命令运行该测试,当前CLI命令代码如下:
@app.cli.command() @click.argument('test_names', nargs=-1) def test(coverage, test_names): """Run the unit tests""" import unittest if test_names: tests = unittest.TestLoader().loadTestsFromNames(test_names) else: tests = unittest.TestLoader().discover('tests') unittest.TextTestRunner(verbosity=2).run(tests)
通过if __name__ == "__main__"运行会绕过现有测试加载/运行流程,因此需要其他解决方案。
解决方案
方法一:使用Werkzeug的run_simple替代app.run()
直接调用Werkzeug底层的服务器启动方法,绕开Flask的app.run()封装,避免CLI冲突:
- 导入
run_simple:
from werkzeug.serving import run_simple
- 修改线程启动代码:
cls.server_thread = threading.Thread( target=run_simple, args=('0.0.0.0', 5000, cls.app), kwargs={ 'use_reloader': False, 'use_debugger': False, 'debug': False } )
方法二:临时禁用CLI冲突检查(不推荐)
通过修改Flask内部状态跳过检查,依赖内部实现,可能随版本失效:
在创建测试app后添加:
cls.app._got_first_request = True
方法三:确保shutdown路由正常工作
测试结束后需要关闭服务器,需确保app中已实现/shutdown路由:
from flask import request @app.route('/shutdown') def shutdown(): func = request.environ.get('werkzeug.server.shutdown') if func is None: raise RuntimeError('Not running with the Werkzeug Server') func() return 'Server shutting down...'
内容的提问来源于stack exchange,提问作者Darwin von Corax
相关产品推荐
相关产品推荐

