Я пытаюсь заменить некоторые старые модульные тесты тестированием на основе свойств (PBT), в частности, scala
и scalatest - scalacheck
, но я думаю, что проблема более общая. Упрощенная ситуация такова, если у меня есть метод, который я хочу протестировать:
def upcaseReverse(s:String) = s.toUpperCase.reverse
Обычно я бы написал модульные тесты, например:
assertEquals("GNIRTS", upcaseReverse("string"))
assertEquals("", upcaseReverse(""))
// ... corner cases I could think of
Итак, для каждого теста я пишу ожидаемый результат, без проблем. Теперь, с PBT, это было бы так:
property("strings are reversed and upper-cased") {
forAll { (s: String) =>
assert ( upcaseReverse(s) == ???) //this is the problem right here!
}
}
Когда я пытаюсь написать тест, который будет верным для всех String
входных данных, я обнаруживаю, что мне приходится снова писать логику метода в тестах. В этом случае тест будет выглядеть так:
assert ( upcaseReverse(s) == s.toUpperCase.reverse)
То есть мне пришлось написать реализацию в тесте, чтобы убедиться, что вывод правильный. Есть ли выход из этого? Я неправильно понимаю PBT, и должен ли я вместо этого тестировать другие свойства, например:
- "строки должны иметь ту же длину, что и оригинал"
- "строки должны содержать все символы оригинала"
- "строки не должны содержать символы нижнего регистра"...
Это также правдоподобно, но звучит как надуманное и менее ясное. Может ли кто-нибудь, у кого больше опыта в PBT, пролить свет?
EDIT: следуя источникам @Eric, я добрался до этот пост, и есть пример того, что я имею в виду (в разделе Применение категорий еще раз): для проверки метода times
в (F#
):
type Dollar(amount:int) =
member val Amount = amount
member this.Add add =
Dollar (amount + add)
member this.Times multiplier =
Dollar (amount * multiplier)
static member Create amount =
Dollar amount
автор заканчивает тем, что пишет тест, который выглядит так:
let ``create then times should be same as times then create`` start multiplier =
let d0 = Dollar.Create start
let d1 = d0.Times(multiplier)
let d2 = Dollar.Create (start * multiplier) // This ones duplicates the code of Times!
d1 = d2
Итак, чтобы проверить метод, код метода дублируется в тесте. В этом случае что-то столь же тривиальное, как умножение, но я думаю, что это экстраполируется на более сложные случаи.