测试Flask WTForms验证器而无需重复代码

时间:2018-10-27 15:11:40

标签: python flask python-unittest flask-wtforms wtforms

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

10-27 22:05:47.881 22671-22767/com.smilebooth.easyshare E/AndroidRuntime: FATAL EXCEPTION: android_2
    Process: com.smilebooth.easyshare, PID: 22671
    java.lang.NoSuchMethodError: No static method catching(Lcom/google/common/util/concurrent/ListenableFuture;Ljava/lang/Class;Lcom/google/common/base/Function;)Lcom/google/common/util/concurrent/ListenableFuture; in class Lcom/google/common/util/concurrent/Futures; or its super classes (declaration of 'com.google.common.util.concurrent.Futures' appears in /data/app/com.smilebooth.easyshare-_OaEo0xawrKXOX90QnGu-Q==/split_lib_dependencies_apk.apk!classes2.dex)
        at com.google.api.core.ApiFutures.catching(ApiFutures.java:77)
        at com.google.photos.library.v1.upload.PhotosLibraryUploadUnaryCallable.futureCall(PhotosLibraryUploadUnaryCallable.java:50)
        at com.google.photos.library.v1.upload.PhotosLibraryUploadUnaryCallable.futureCall(PhotosLibraryUploadUnaryCallable.java:31)
        at com.google.api.gax.rpc.UnaryCallable.futureCall(UnaryCallable.java:87)
        at com.google.api.gax.rpc.UnaryCallable.call(UnaryCallable.java:112)
        at com.google.photos.library.v1.PhotosLibraryClient.uploadMediaItem(PhotosLibraryClient.java:125)
        at com.smilebooth.data.repository.ShareDataRepository.uploadPhotoContent(ShareDataRepository.kt:201)
        at com.smilebooth.data.repository.ShareDataRepository.share(ShareDataRepository.kt:104)
        at com.smilebooth.domain.usecase.ShareUseCase$buildUseCaseObservable$1.subscribe(ShareUseCase.kt:21)
        at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:260)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

1 个答案:

答案 0 :(得分:2)

我建议您创建一个自定义断言方法(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最顶层的代码示例更加清晰。