Метод тестирования JUnit с рандомизированной природой

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

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

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

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

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

Удачи,

Pete

10
задан 30 May 2010 в 12:57
поделиться

8 ответов

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

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

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

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

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

5
ответ дан 4 December 2019 в 01:00
поделиться

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

  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.

3
ответ дан 4 December 2019 в 01:00
поделиться

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

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

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

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

1
ответ дан 4 December 2019 в 01:00
поделиться

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

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

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

0
ответ дан 4 December 2019 в 01:00
поделиться

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

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

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

2
ответ дан 4 December 2019 в 01:00
поделиться

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

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

.
0
ответ дан 4 December 2019 в 01:00
поделиться

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

0
ответ дан 4 December 2019 в 01:00
поделиться

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

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

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

0
ответ дан 4 December 2019 в 01:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: