Генерация уникальных строк в FsCheck

Мне нужно сгенерировать уникальные строки, отличные от null, которые будут использоваться в качестве ключей Dictionary. Я пробовал что-то вроде:

 public static Gen<NonNull<string>> UniqueStrings()
 {
     return from s in Arb.Default.NonNull<string>().Generator
            select s;
 }

Затем я использую UniqueString() в:

public static Arb<Foo> Foos()
{
    // Foo's constructor will use the string parameter
    // as key to an internal Dictionary
    return (from nonNullString in UniqueStrings()
            select new Foo(nonNullString.Item)).ToArbitrary();
}

Однако я получаю исключение при проверке свойств Foo, потому что FsCheck иногда генерирует одну и ту же строку дважды, что приводит к ошибке DuplicateKeyException.

Как я могу сгенерировать уникальные строки для передачи в конструктор Foo?


person rexcfnghk    schedule 15.01.2016    source источник
comment
Я что-то пропустил? Guid.NewGuid().ToString(); на основе GUID будет уникальной строкой.   -  person Dmitry Bychenko    schedule 15.01.2016


Ответы (3)


Вы не можете заставить генератор FsCheck генерировать уникальные значения, потому что у вас по сути нет доступа к истории ранее сгенерированных значений, и сам FsCheck не гарантирует уникальность.

Что вы можете сделать в этом случае, так это сгенерировать, скажем, список строк, а затем сделать список уникальным, используя, например, Distinct(). Затем вы также можете создать список Foo, используя аналогичный подход.

Например:

Gen<Foo[]> res = from s in Arb.Default.Set<string>().Generator
                 select s.Select(ss => new Foo(ss)).ToArray();

(Обратите внимание, что вы не можете использовать from для получения ss, потому что C# не позволяет вам смешивать разные методы LINQ, один на Gen, другой на IEnumerable)

Кстати, мне интересно, не является ли это дополнительным свойством, которое вы хотите проверить. Если пользователь Foo должен дать ему уникальную строку, как это поддерживается? Что произойдет, если они этого не сделают?

person Kurt Schelfthout    schedule 15.01.2016
comment
Не могли бы вы показать пример того, как сделать уникальным список строк, а затем передать его конструктору Foo? Я пробовал что-то вроде from ss in Gen.Default.Set<NonNull<string>>().Generator from s in ss select new Foo(s), но получаю сообщение об ошибке компиляции, говорящее, что для типа Gen нет SelectMany - person rexcfnghk; 18.01.2016
comment
Я имею в виду Arb.Default - person rexcfnghk; 18.01.2016
comment
Конструктор Foo вставит string во внутренний Dictionary, поэтому, если параметр string не уникален, он выдаст исключение. ИМХО, это не лучший дизайн, потому что предварительные условия настолько неясны, но у меня нет другого выбора, кроме как придерживаться этой реализации. Все, что я могу сделать, это создать больше тестов в надежде выявить его недостатки. - person rexcfnghk; 20.01.2016

Для создания уникальных строк вы можете использовать Guid, это стандартный способ создания уникальных строк даже на нескольких компьютерах.

person Carra    schedule 15.01.2016
comment
Хотя идентификаторы уникальны, они также имеют одинаковую длину и набор символов, которых может быть или не хватить для тестирования алгоритма с достаточно разнообразным набором входных данных (нулевые значения, длинные строки, специальные символы и т. д.). - person Stefano Ricciardi; 07.07.2021

Вместо создания уникальных строк вы можете добавить простую проверку перед вставкой в ​​словарь.

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

person Max Kilovatiy    schedule 15.01.2016
comment
Я изо всех сил стараюсь не изменять реализацию Foo, потому что я просто хочу, чтобы FsCheck не генерировал идентичные ключи string. Изменение реализации Foo похоже на уход от вопроса вместо его решения. - person rexcfnghk; 15.01.2016