Извините, что прыгнул через несколько месяцев, но, поскольку этот вопрос легко появляется в Google, я думаю, что ему нужен лучший ответ.
Ответ Ивана о модульных тестах, когда вы говорите о тестах свойствтак что давайте не будем обращать на это внимание.
Dfeuer сообщает вам, когда допустимо зеркалировать реализацию, но не о том, что делать для вашего варианта использования.
Это распространенная ошибка в тестах на основе свойств (PBT)сначала переписать код реализации.Но это не то, для чего нужны PBT.Они существуют для проверки свойств вашей функции.Эй, не волнуйтесь, мы все делаем эту ошибку в первые несколько раз, когда мы пишем PBT: D
Тип свойства, который вы можете здесь проверить, является ли ваш ответ функции непротиворечивым с егоinput:
if SUT.shouldDiscountProduct p t
then isJust (userDiscountCode p) && isJust (productDiscount t)
else isNothing (userDiscountCode p) || isNothing (productDiscount t)
Это тонкий в вашем конкретном случае использования, но обратите внимание, мы изменили логику.Ваш тест проверяет ввод и, основываясь на этом, подтверждает вывод.Мой тест проверяет на выходе и, основываясь на этом, утверждает на входе.В других случаях использования это может быть гораздо менее симметричным.Большая часть кода также может быть реорганизована, я дам вам это упражнение;)
Но вы можете найти и другие типы свойств!Например, неизменность свойства:
SUT.shouldDiscountProduct p{userDiscountCode = Nothing} t == False
SUT.shouldDiscountProduct p{productDiscount = Nothing} t == False
Посмотрите, что мы здесь сделали?Мы зафиксировали одну часть ввода (например, код скидки пользователя всегда пуст) и утверждаем, что независимо от того, как все остальное меняется, вывод является инвариантным (всегда ложным).То же самое касается скидки на продукт.
Последний пример: вы можете использовать аналогичное свойство , чтобы проверить ваш старый код и ваш новый код ведет себя точно так же:
shouldDiscountProduct user product =
if M.isNothing (userDiscountCode user)
then False
else if (productDiscount product) then True
else False
shouldDiscountProduct' user product
| Just _ <- userDiscountCode user
, Just _ <- productDiscount product
= True
| otherwise = False
SUT.shouldDiscountProduct p t = SUT.shouldDiscountProduct' p t
Что гласит: «Независимо от ввода, переписанная функция всегда должна возвращать то же значение, что и старая функция».Это очень круто при рефакторинге!
Надеюсь, это поможет вам понять идею тестов на основе свойств: перестаньте так сильно беспокоиться о значении, возвращаемом вашей функцией, и начните задумываться о некоторых режимах, которые ваша функция имеет.
Обратите внимание, PBT не являются врагом юнит-тестов, они действительно хорошо сочетаются друг с другом.Вы можете использовать 1 или 2 модульных теста, если вы чувствуете себя более уверенно в отношении фактических значений, а затем написать тест (ы) свойств, чтобы утверждать, что ваша функция имеет некоторые поведения, независимо от ввода.