Вопрос

Мне удалось получить xUnit ( Единица измерения ) работаю над своим маленьким образцом сборки.Теперь я хочу посмотреть, смогу ли я грокнуть Проверка FsCheck слишком.Моя проблема в том, что я в тупике, когда дело доходит до определения тестовых свойств для моих функций.

Может быть, у меня просто нет хорошего примерного набора функций, но какие были бы хорошие тестовые свойства для этих функций, например?

//transforms [1;2;3;4] into [(1,2);(3,4)]
pairs : 'a list -> ('a * 'a) list      //'

//splits list into list of lists when predicate returns 
//  true for adjacent elements
splitOn : ('a -> 'a -> bool) -> 'a list -> 'a list list

//returns true if snd is bigger
sndBigger : ('a * 'a) -> bool (requires comparison)

Нет правильного решения

Другие советы

Уже есть много конкретных ответов, поэтому я попытаюсь дать несколько общих ответов, которые могут натолкнуть вас на некоторые идеи.

  1. Индуктивные свойства для рекурсивных функций.Для простых функций это, вероятно, равносильно повторной реализации рекурсии.Тем не менее, сделайте это простым:в то время как фактическая реализация чаще всего эволюционирует (напримерэто становится хвостовой рекурсией, вы добавляете запоминание, ...) сохраняйте свойство простым.Здесь обычно пригодится комбинатор свойств ==> .Ваша функция pairs может стать хорошим примером.
  2. Свойства, которые относятся к нескольким функциям в модуле или типе.Обычно это имеет место при проверке абстрактных типов данных.Например:добавление элемента в массив означает, что массив содержит этот элемент.Это проверяет согласованность Array.add и Array.contains.
  3. Поездки туда и обратно:это хорошо для конверсий (напримерparsing, сериализация) - генерирует произвольное представление, сериализует его, десериализует, проверяет, что оно равно исходному.Возможно, вы сможете сделать это с помощью splitOn и concat.
  4. Общие свойства как проверка на вменяемость.Ищите общеизвестные свойства, которые могут сохраняться - такие вещи, как коммутативность, ассоциативность, идемпотентность (применение чего-либо дважды не меняет результата), рефлексивность и т.д.Идея здесь скорее в том, чтобы немного потренировать функцию - посмотреть, не делает ли она чего-нибудь действительно странного.

В качестве общего совета постарайтесь не придавать этому слишком большого значения.Для sndBigger хорошим свойством было бы:

пусть `должно возвращать true тогда и только тогда, когда snd больше` (a: int) (b: int) = sndBigger (a,b) = b > a

И это, вероятно, именно та реализация.Не беспокойтесь об этом - иногда простой, старомодный модульный тест - это как раз то, что вам нужно.Не нужно испытывать чувство вины!:)

Может быть эта ссылка (командой Pex) также высказывает некоторые идеи.

Я начну с sndBigger - это очень простая функция, но вы можете написать некоторые свойства, которые должны относиться к ней.Например, что происходит, когда вы меняете местами значения в кортеже:

// Reversing values of the tuple negates the result
let swap (a, b) = (b, a)
let prop_sndBiggerSwap x = 
  sndBigger x = not (sndBigger (swap x))

// If two elements of the tuple are same, it should give 'false'
let prop_sndBiggerEq a = 
  sndBigger (a, a) = false

Редактировать: Это правило prop_sndBiggerSwap не всегда выполняется (см. Комментарий от квб).Однако следующее должно быть правильным:

// Reversing values of the tuple negates the result
let prop_sndBiggerSwap a b = 
  if a <> b then 
    let x = (a, b)
    sndBigger x = not (sndBigger (swap x))

Что касается pairs функция, квб уже опубликовал несколько хороших идей.Кроме того, вы могли бы проверить, что превращение преобразованного списка обратно в список элементов возвращает исходный список (вам нужно будет обработать случай, когда входной список нечетный - в зависимости от того, какой pairs функция должна работать в этом случае):

let prop_pairsEq (x:_ list) = 
  if (x.Length%2 = 0) then
    x |> pairs |> List.collect (fun (a, b) -> [a; b]) = x
  else true

Для splitOn, мы можем протестировать аналогичную вещь - если вы объедините все возвращенные списки, это должно привести к получению исходного списка (это не проверяет поведение разделения, но с этого стоит начать - это, по крайней мере, гарантирует, что никакие элементы не будут потеряны).

let prop_splitOnEq f x = 
  x |> splitOn f |> List.concat = x

Я не уверен, что Проверка FsCheck может справиться с этим, хотя (!) потому что свойство принимает функцию в качестве аргумента (поэтому ему нужно было бы генерировать "случайные функции").Если это не сработает, вам нужно будет предоставить пару более специфических свойств с помощью какой-нибудь рукописной функции f.Далее, реализуем проверку того, что f возвращает true для всех смежных пар в разделенных списках (как квб предполагает) на самом деле не так уж и сложно:

let prop_splitOnAdjacentTrue f x = 
  x |> splitOn f 
    |> List.forall (fun l -> 
         l |> Seq.pairwise 
           |> Seq.forall (fun (a, b) -> f a b))

Вероятно, единственное последнее, что вы могли бы проверить, это то, что f ВОЗВРАТ false когда вы даете ему последний элемент из одного списка и первый элемент из следующего списка.Следующее не является полностью полным, но оно показывает, как действовать:

let prop_splitOnOtherFalse f x = 
  x |> splitOn f
    |> Seq.pairwise 
    |> Seq.forall (fun (a, b) -> lastElement a = firstElement b)

Последний пример также показывает, что вам следует проверить, является ли splitOn функция может возвращать пустой список как часть возвращаемого списка результатов (потому что в этом случае вы не смогли найти первый / последний элемент).

Для некоторого кода (например sndBigger), реализация настолько проста, что любое свойство будет, по крайней мере, таким же сложным, как исходный код, поэтому тестирование с помощью FsCheck может не иметь смысла.Однако для двух других функций вот некоторые вещи, которые вы могли бы проверить:

  • pairs
    • Что ожидается, когда исходная длина не делится на два?Вы могли бы проверить, не генерируется ли исключение, если это правильное поведение.
    • List.map fst (pairs x) = evenEntries x и List.map snd (pairs x) = oddEntries x для простых функций evenEntries и oddEntries который вы можете написать.
  • splitOn
    • Если я понимаю ваше описание того, как должна работать функция, то вы могли бы проверить условия типа "Для каждого списка в результате splitOn f l, никакие две последовательные записи не удовлетворяют f" и "Спискам взятия (l1,l2) От splitOn f l попарно, f (last l1) (first l2) удерживает".К сожалению, логика здесь, вероятно, будет сравнима по сложности с самой реализацией.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top