Метод испытаний JUnit с рандомизированной природой - PullRequest
10 голосов
/ 30 мая 2010

Я сейчас работаю над небольшим проектом для себя и использую его как возможность познакомиться с модульным тестированием и ведением надлежащей документации.

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

Реализация очень проста и, безусловно, будет работать:

public void shuffle()
{
    Collections.shuffle(this.cards);
}

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

Приветствия

Пит

Ответы [ 9 ]

5 голосов
/ 30 мая 2010

Утверждение, что ваш метод перемешивания на самом деле тасует карты, очень сложно, если не невозможно. Генераторы случайных чисел по умолчанию являются случайными только в определенной степени. Невозможно проверить, довольны ли вы этой степенью случайности, потому что это займет слишком много времени. На самом деле вы тестируете сам генератор случайных чисел, который не имеет особого смысла.

Однако вы можете проверить инварианты этого метода.

  • Если вы выйдете из метода, в колоде должно быть ровно столько же карт, сколько было при вводе.
  • Метод случайного выбора не должен вводить дубликаты.

Конечно, вы можете создать тест, который проверяет, что в последовательности n тасований не возвращаются повторяющиеся колоды. Но время от времени этот тест может проваливаться (хотя вряд ли, как уже говорилось в других ответах).

Что-то еще, что нужно принять во внимание, - это сам генератор случайных чисел. Если это всего лишь игрушечный проект, достаточно java.util.Random. Если вы намереваетесь создать какую-нибудь карточную онлайн-игру, рассмотрите возможность использования java.security.SecureRandom.

3 голосов
/ 30 мая 2010

Во-первых, давайте подумаем о вероятностях:

  1. Вы не можете гарантировать, что при перемешивании карты не будут размещены в точном порядке. Однако вероятность сделать это с колодой из 52 карт составляет 1/52! (т.е. это минимально и, вероятно, не стоит беспокоиться.)

  2. Вам определенно нужно будет проверить всю колоду, хотя вероятность того, что верхняя карта будет такой же, как и до перемешивания, составляет 1 / 52.

В общем случае и при условии, что вы используете генератор чисел java.util.Random, просто инициализируйте его тем же начальным числом. Затем выход для предварительно определенного ввода должен быть повторяемым.

Однако, специально для этого случая, предполагая, что вы не реализовали свой собственный List Я не вижу смысла в тестировании Collections.shuffle(List<?> list) или Collections.shuffle(List<?> list, Random rnd) ( API link ), так как они являются лишь частью Java API.

2 голосов
/ 30 мая 2010

Другой подход заключается в использовании метода shuffle(List<?> list, Random random) и внедрении экземпляра Random, засеянного константой.

Таким образом, ваш тест JUnit может выполнить серию вызовов и проверить, чтобы результат был ожидаемым.

Обычная реализация вашего класса создаст экземпляр Random, который не будет заполнен.

1 голос
/ 30 мая 2010

Вы фактически делегируете всю тяжелую работу классу java.util.Collections. Это центральный класс в API коллекции Java, и вы должны просто предположить, что он работает так же, как вы, вероятно, делаете с классом java.lang.String.

Я бы порекомендовал кодировать интерфейсы и макетировать / заглушки свой класс реализации с помощью метода shuffle(). Тогда вы можете просто утверждать, что ваши вызовы по методу shuffle() на самом деле вызываются из вашего теста, а не тестировать точно так же, как парни Sun / Oracle тестировали ранее.

Это позволяет вам больше сосредоточиться на тестировании своего собственного кода, где, вероятно, находится 99,9% всех ошибок. И если вы, например, замените метод java.util.Collections.shuffle() на метод из другой среды или собственную реализацию, ваш интеграционный тест все равно будет работать!

Я понимаю, что вы делаете это, потому что вы хотите учиться, и я полагаю, что знания о заглушении / отрыве логики от других структур очень полезны как часть ваших знаний по тестированию.

0 голосов
/ 21 марта 2013

Вы можете перетасовать несколько раз, отслеживая, сколько раз туз пик (или какая-либо другая карта, или все другие карты) оказывается первой картой в колоде. Теоретически карта должна оказаться сверху примерно на 1 из 52 тасовок. После того, как все данные собраны, сравните фактическую частоту с числом 1/52 и убедитесь, что разница (абсолютное значение) меньше некоторого выбранного значения эпсилона. Чем больше вы тасуете, тем меньше может быть значение вашего эпсилона. Если ваш метод shuffle () помещает карту сверху в пределах вашего порога epsilon, вы можете быть уверены, что она рандомизирует карты так, как вам бы хотелось.

И вам не нужно останавливаться только на верхней карте. Вы можете проверить, дает ли каждая локация в колоде одинаковые результаты. Делайте это с одной картой, делать это будут все карты, это, вероятно, не имеет значения. Это может быть излишним, но это гарантирует, что ваш shuffle () работает правильно.

0 голосов
/ 30 мая 2010

Я работал со случайными числами в среде моделирования и симуляции и стоял перед похожей проблемой: как я могу на самом деле тестировать наши реализации PRNG. В конце концов, я на самом деле этого не сделал. Вместо этого я выполнил несколько проверок работоспособности. Например, все наши PRNG объявляют, сколько битов они сгенерировали, поэтому я проверил, действительно ли эти биты изменились (с итерациями 10k или около того), а все остальные биты равны 0. Я проверил правильное поведение относительно начальных чисел (инициализация PRNG с тем же seed должен генерировать ту же последовательность чисел) и т. д. Затем я решил поместить реальные тесты на случайность в интерактивный пользовательский интерфейс, чтобы их можно было проверять при желании, но для модульных тестов недетерминированный результат не так хорош, подумал я.

0 голосов
/ 30 мая 2010

Большинство людей считают, что вы должны проверить то, что тестируете. Под этим я подразумеваю то, что вы создаете (или интегрируете, когда вы убедитесь, что сторонняя библиотека действительно выполняет то, что говорит).

Но вы не должны проверять сам язык Java.

Должен быть какой-то принцип тестирования, например "Не тестировать PlusEquals".

0 голосов
/ 30 мая 2010

Интересный вопрос.На мой взгляд, лучшим способом было бы сохранить каждое «перемешивание» в коллекции, а затем сравнивать после каждого «перемешивания», соответствует ли ваша колода какой-либо из предыдущих «колод» в коллекции.

В зависимости от количества «Случайности», которое вам требуется, вы увеличите количество перемешанных колод, которые вы храните в этом модульном тесте, т.е. после 50 перемешиваний у вас будет коллекция из 50 «колод»

0 голосов
/ 30 мая 2010

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

Итак, ответ: проверьте, отличается ли порядок для всей колоды.

Кроме того, я думаю, что для вашего метода shuffle () можно смело требовать не возвращать карты в одном и том же порядке два раза подряд. И если вы хотите абсолютно точно выполнить это требование, вы можете проверить сходство в реализации метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...