Какова лучшая практика при написании Perl-тестов, включающих случайность? - PullRequest
6 голосов
/ 19 апреля 2011

Работая над некоторыми обновлениями для моего модуля List :: Gen , я решил добавить метод ->pick(num), который будет возвращать список случайных элементов размером num из его источника.Чтобы проверить это, я использовал srand для заполнения генератора случайных чисел и сделал несколько тестов вида:

srand 1234;
is $src->pick(5)->str, '3 6 1 7 9';

И все это хорошо работало на компьютере с Windows, на котором я работал в то время.Однако, когда я переместил проект на рабочую станцию ​​Mac, все тесты на случайность не прошли, так как, несмотря на то же случайное начальное число, rand давал разные результаты.Я понял, что это из разных базовых реализаций C rand().

Итак, вопрос в том, каков лучший кроссплатформенный способ тестирования этих функций?Должен ли я перегрузить функцию rand своей собственной?Должен ли я встроить хуки в функции, которые используют rand, чтобы включить режим «тестирования», который дает предсказуемый результат?Существуют ли другие методы?

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

Test :: Random и Test :: MockRandom похоже на предложения CPAN, есть ли у кого-нибудь опыт работы с этими модулями?

Ответы [ 4 ]

2 голосов
/ 19 апреля 2011

Я не использовал ни того, ни другого.

Похоже, Test :: Random был бы лучшим выбором для вас, поскольку вы, очевидно, просто используете случайность в своем тестировании, а не в выпущенном коде. Это должно быть намного проще в использовании.

Модуль Test :: MockRandom заставляет функцию rand () возвращать детерминированную последовательность.

0 голосов
/ 20 апреля 2011

Может быть, случайная часть не имеет значения для ваших тестов?

Проходящие тесты могут проверить следующее:

  1. ли -> pick (X) вернуть X элементов?
  2. Являются ли все элементы X частью списка $ src?
  3. Тест на 0, 1 и т. Д. *
  4. (может быть?) Проверьте, что два разных семени srand возвращают разные списки

Это, по сути, то, что вы уже делаете, поскольку вы пытаетесь убрать rand () из уравнения. Можно также пройти весь путь и проверить, что ваша функция выполняет то, что говорит на жестяной банке.

0 голосов
/ 20 апреля 2011

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

Применительно к домену случайных чисел у нас есть что-то вроде:

use strict;
use warnings;

package Foo;

sub new {
    my ($class) = @_;

    return bless {} => $class;
}

sub get_random_number {
    return rand();
}

package main;
use Test::MockObject::Extends;
use Test::More tests => 1;

my $foo = Test::MockObject::Extends->new( Foo->new() );
$foo->set_series(get_random_number => 0.5, 0.001, 0.999);

is( $foo->get_random_number, 0.5 );

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

В случае вашей конкретной проблемы вам нужно вынести зависимость на rand из pick, а затем переопределить извлеченный метод в тестируемой версии List::Gen. Test::MockObject::Extends идеально подходит для этой цели.

0 голосов
/ 19 апреля 2011

Вы можете запустить несколько пиков и убедиться, что они не возвращают одно и то же. Это цель функции, в конце концов.

...