Как генерировать нулевые строки для тестов FsCheck

Использование FsCheck, F#-версии библиотеки тестов Haskell QuickCheck, для создания тестов из C# я обнаружил, что генератор случайных строк не генерирует нулевую строку.

using FsCheck.Fluent;
Spec.ForAny<string>(s => s != null).QuickCheck(); // always pass

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

var strings = Any.ValueIn<string>(null, "non-null string");
Spec.For(strings, s => true).QuickCheck(); // throws null ref exception

И строки представляют собой особый случай, потому что они обрабатывают специально созданные объекты, такие как

class Thing {}

при смешивании с нулевыми значениями:

var objects = Any.ValueIn(null, new Thing());
Spec.For(objects, s => true).QuickCheck(); // pass

person Alapago    schedule 22.04.2014    source источник


Ответы (2)


Я попытался немного разобраться в этом, и оказалось, что вы обнаружили ошибку в FsCheck.

Похоже, что проблема в файле Arbitrary.fs и на самом деле связана только со строками. Мне пришлось заменить это, где они вызывают ToCharArray в строке

    static member String() = 
        { new Arbitrary<string>() with
            override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate
            override x.Shrinker s = s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars))
        }

с этим

    static member String() = 
        { new Arbitrary<string>() with
            override x.Generator = Gen.map (fun chars -> new String(List.toArray chars)) generate
            override x.Shrinker s = 
                match s with
                    | null  -> seq {yield null;}
                    | _ -> s.ToCharArray() |> Array.toList |> shrink |> Seq.map (fun chars -> new String(List.toArray chars))
        }

Вы можете обсудить это с разработчиками fscheck здесь, а также проверить, хорошо ли работает мое исправление — есть вероятно, лучший способ реализовать это, но это было бы проще для тех, кто уже знает код.

person Tomas Pastircak    schedule 23.04.2014
comment
Если это ошибка, то она может вызвать проблемы, если она будет исправлена. Таким образом, они могут обновить его до функции. - person Alapago; 23.04.2014
comment
@user2046431 user2046431 Я сильно в этом сомневаюсь, так как этот случай будет неудачным только для null string. И даже если они решат обновить его до функции, они, по крайней мере, могут захотеть отслеживать его где-то на своем сайте. - person Tomas Pastircak; 23.04.2014
comment
Согласен, что это должно быть отправлено в их систему отслеживания проблем, и что исправление сжатия, чтобы оно не захлебывалось нулями, ничего не сломает. Я думал об ошибке, когда по умолчанию не генерируются нули. - person Alapago; 23.04.2014
comment
Почему бы вам не отправить запрос на вытягивание. Конечно, я бы согласился с исправлением. Я не уверен в генерации нулей; Исторически основной целью FsCheck был F#, и, как правило, пустые значения не представляют большой проблемы. - person Kurt Schelfthout; 24.04.2014
comment
Обратите внимание, что в FsCheck 2.x нулевые значения генерируются и корректно сокращаются для строки. - person Kurt Schelfthout; 13.10.2015

Для FsCheck 1.x я нашел решение, которое включает изменение генератора случайных строк по умолчанию:

public class MyArbitraries
{
    public static Arbitrary<string> String()
    {
        var nulls = Any.Value<string>(null);
        var nonnulls = Arb.Default.String().Generator;
        return Any.GeneratorIn(nulls, nonnulls).ToArbitrary;
    }
}

а затем инициализируйте его с помощью:

DefaultArbitraries.Add<MyArbitraries>();

Затем тест в вопросе терпит неудачу, как и предполагалось:

Spec.ForAny<string>(s => s != null).QuickCheck() // now fails, which is good

Это создаст около 50% нулей и 50% случайных строк, веса можно настроить:

Spec.ForAny<string>(s => true)
    .Classify(s => s==null, "null")
    .Classify(s => s!=null, "not null")
    .QuickCheck(); // displays percentages

Однако эффективное переопределение генератора строк по умолчанию может быть плохой идеей, если решение не включать нулевое значение по умолчанию было преднамеренным, а не ошибкой в ​​библиотеке. И, если бы это была ошибка, она исказила бы дистрибутивы при исправлении.

person Alapago    schedule 23.04.2014
comment
Мне никак не удается найти DefaultArbitraries где этот парень? - person Maslow; 12.10.2015
comment
Этот ответ применим только к FsCheck 1.x. В версии 2.x вместо этого следует использовать Arb.register. Кроме того, в версии 2.x для строк генерируются пустые значения. - person Kurt Schelfthout; 13.10.2015