简体   繁体   中英

Testing Flask WTForms validators without repeating code

I have written a relatively simple web app using Flask and WTForms with standard and custom form validators. I am now trying to learn Python testing. I have written the following tests.py file that works fine with unittest, but has redundancy. Is there a better, more effecient, more pythonic (DNRY) way to do this? I've looked at some pytest tutorials and wonder if fixtures would help, but I cannot quite understand them. And I assume there is a more pythonic way to do this with unittest. It seems I need a method to which I can pass the different dict arguments, but I cannot figure out how to do that.

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()

I suggest you create a custom asserter method ( assertExtensiveFlightSearchGivesError(self, data, expected_error_message) ) to remove some of the duplication:

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

You could also collect all the test cases together and use .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)

But in your case, IMO the topmost code example is clearer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM