在测试

时间:2016-12-09 17:14:10

标签: python flask

我正在为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,包括“”,它只生成一个格式错误的网址,但仍然与响应中生成的网址不匹配。

除了对网址进行硬编码外,还有其他方法可以解决这个问题吗?

3 个答案:

答案 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