在不重复代码的情况下测试 Flask WTForms 验证器

Testing Flask WTForms validators without repeating code

我使用 Flask 和 WTForms 以及标准和自定义表单验证器编写了一个相对简单的 Web 应用程序。我现在正在努力学习 Python 测试。我编写了以下 tests.py 文件,它可以与 unittest 一起正常工作,但有冗余。有没有更好、更有效、更 pythonic (DNRY) 的方法来做到这一点?我看过一些 pytest 教程,想知道固定装置是否有帮助,但我不太理解它们。而且我假设有一种更 pythonic 的方法可以用 unittest 来做到这一点。看来我需要一种方法来传递不同的 dict 参数,但我不知道该怎么做。

from fly_app import app
import unittest

class FlaskTestCase(unittest.TestCase):

    def test_city_code(self):
        tester = app.test_client(self)
        response = tester.post('/flight_search/extensive', data=dict(origin="xxxx"))
        self.assertIn(b'That does not appear to be a valid city code', response.data)

    def test_code_pairs(self):
        tester = app.test_client(self)
        response = tester.post('/flight_search/extensive', data=dict(origin="HFD", destination="CAS"))
        self.assertIn(b'This origin-destination pair is not in searchable cache', response.data)

    def test_duration_range(self):
        tester = app.test_client(self)
        response = tester.post('/flight_search/extensive', data=dict(origin="MSP", destination="NYC", min_duration=20))
        self.assertIn(b'Number must be between 1 and 15', response.data)

    def test_duration_integer(self):
        tester = app.test_client(self)
        response = tester.post('/flight_search/extensive', data=dict(origin="MSP", destination="NYC", min_duration='abc'))
        self.assertIn(b'Not a valid integer value', response.data)

    def test_duration_pair(self):
        tester = app.test_client(self)
        response = tester.post('/flight_search/extensive', data=dict(origin="MSP", destination="NYC", min_duration=10, max_duration=7))
        self.assertIn(b'Maximum trip length cannot be less than minimum', response.data)

if __name__ == "__main__":
    unittest.main()

我建议您创建自定义断言器方法 (assertExtensiveFlightSearchGivesError(self, data, expected_error_message)) 以删除一些重复项:

class FlaskTestCase(unittest.TestCase):
    def assertExtensiveFlightSearchGivesError(self, data, expected_error_message):
        response = test_client.post("/flight_search/extensive", data=data)
        self.assertEqual(4xx, response.status_code)
        self.assertIn(expected_error_message, response.data, "extensive flight search did not contain expected error message")

    def test_city_code(self):
        self.assertExtensiveFlightSearchGivesError(
            {'origin': 'xxx'},
            b'That does not appear to be a valid city code'
        )

    def test_code_pairs(self):
        self.assertExtensiveFlightSearchGivesError(
            {'origin': "HFD", 'destination': "CAS"},
            b'This origin-destination pair is not in searchable cache'
        )

    # ... and so on

您也可以将所有测试用例收集在一起并使用 .subTest():

TEST_CASES = [
    ({'origin': 'xxx'}, b'That does not appear to be a valid city code'),
    ({'origin': "HFD", 'destination': "CAS"}, b'This origin-destination pair is not in searchable cache')
]

class FlaskTestCase2(unittest.TestCase):
    def assertExtensiveFlightSearchGivesError(self, data, expected_error_message):
        response = test_client.post("/flight_search/extensive", data=data)
        self.assertEqual(4xx, response.status_code)
        self.assertIn(expected_error_message, response.data, "extensive flight search did not contain expected error message")

    def test_all_error_cases(self):
        for data, expected_error_message in TEST_CASES:
            with self.subTest():
                self.assertExtensiveFlightSearchGivesError(data, expected_error_message)

但在您的情况下,IMO 最上面的代码示例更清晰。