我正在为Flask应用程序编写一些测试,并且遇到了一个问题,即test_client响应中的URL与通过在测试用例中调用url_for产生的URL不匹配。例如,我的模板中有以下内容:
<li><a href="#" onClick="requestReport('{{ url_for('report') }}', '{{ session['email'] }}');">Report</a></li>
test_client的响应将其呈现为:
<li><a href="#" onClick="requestReport('/api/report', 'user@domain.com');">Report</a></li>
我有一个测试用例,用于检查以确保在某些条件下此网址出现在页面上:
self.client = self.app.test_client(use_cookies=True)
with self.app.app_context():
page = self.client.get(url_for("index"), follow_redirects=True).data.decode()
assert url_for("report") in page
问题是,即使URL出现在页面上,此测试也会失败,因为模板中的url_for调用产生的输出与我的测试用例中的url_for调用不同。如果我从测试用例代码中打印url_for(“report”),我得到:
http://localhost:5000/api/report
我将app.config中的SERVER_NAME键设置为“localhost:5000”,因为如果我没有设置SERVER_NAME,则测试用例代码会抛出此错误:
RuntimeError: Application was not able to create a URL adapter for request independent URL generation. You might be able to fix this by setting the SERVER_NAME config variable.
显然,我可以通过在我的测试用例代码中对URL进行硬编码来解决这个问题,但我更倾向于使用URL_for,以便将来对我的URL的更改不会破坏我的测试代码。
我尝试了几个不同的字符串作为SERVER_NAME,包括“”,它只生成一个格式错误的网址,但仍然与响应中生成的网址不匹配。
除了对网址进行硬编码外,还有其他方法可以解决这个问题吗?
答案 0 :(得分:7)
对于API测试,您可以使用test_request_context
class TestFlaskApi(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app_context = self.app.test_request_context()
self.app_context.push()
self.client = self.app.test_client()
def test_hello(self):
response = self.client.get(url_for('api.hello'),
content_type='text')
self.assertEqual(response.get_data(as_text=True), 'hello')
答案 1 :(得分:1)
如果您在应用程序上下文中使用url_for
,就像在测试代码中一样,Flask会自动假定您要创建一个包含完整主机名的外部链接(在SERVER_NAME
配置变量中设置)。而模板中的url_for
将创建没有主机名的内部链接。所以他们不会匹配。要检查是否相等,您需要将_external
属性显式设置为False
。
self.client = self.app.test_client(use_cookies=True)
with self.app.app_context():
page = self.client.get(url_for("index"), follow_redirects=True).data.decode()
assert url_for("report", _external=False) in page
答案 2 :(得分:1)
最佳答案推送了测试请求上下文,但没有在 tearDown
方法中弹出它。正确的设置和拆卸应该是这样的:
class TestFlaskApi(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.context = self.app.test_request_context()
self.context.push() # push it
self.client = self.app.test_client()
def tearDown(self):
self.context.pop() # pop it
def test_hello(self):
response = self.client.get(url_for('api.hello'), content_type='text')
self.assertEqual(response.get_data(as_text=True), 'hello')
如果您使用的是 pytest,请查看 this answer。