Поблочное тестирование - как я тестирую функцию, которая возвращает случайный вывод?

У меня есть функция, которая берет в двух параметрах и возвращается один или другие 50% времени.

Модульный тест на это должен решить, что могли быть возвращены оба параметра. К счастью я не должен доказывать, что вероятность для каждого составляет 50%, но я действительно должен показать, что оба параметра возможны быть возвращенными.

Как я пишу тестовый сценарий для этой функции?

14
задан Extrakun 11 April 2010 в 17:53
поделиться

6 ответов

Если случайность равна на основе генератора случайных чисел, который он вызывает, вы можете настроить функцию-заглушку random (), которая возвращает различные результаты, чтобы дать вам ожидаемые результаты.

21
ответ дан 1 December 2019 в 07:51
поделиться

Вы можете сделать одно из двух:

  1. создать заглушку генератора random (), который может возвращать согласованные числа при модульном тестировании.
  2. запустите ваш код N раз, где N достаточно велико для получения согласованных результатов, пока код работает.

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

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

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

2
ответ дан 1 December 2019 в 07:51
поделиться

Не уверен, что это лучший способ, но я бы использовал цикл while с двумя флагами, которые я установил на основе значений Я получаю от функции. Я бы также использовал какое-то ограничение, чтобы тест не зашел в бесконечный цикл, если тест не прошел (т.е. не получил одно из двух значений). Итак:

int limit = 100;
int i = 0;
while(!firstValueReceived && !secondValueReceived && i < limit) {
   int value = randomFunc();

   if(!firstValueReceived) {
      firstValueReceived = (value == firstValue);
   }

   if(!secondValueReceived) {
      secondValueReceived = (value == secondValue);
   }
   i++;
}

assertTrue(firstValueReceived && secondValueReceived);

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

while(!firstValueReceived && !secondValueReceived) {
   int value = randomFunc();

   if(!firstValueReceived) {
      firstValueReceived = (value == firstValue);
   }

   if(!secondValueReceived) {
      secondValueReceived = (value == secondValue);
   }
}

assertTrue(firstValueReceived && secondValueReceived);
3
ответ дан 1 December 2019 в 07:51
поделиться

Вы проверяете его через разделение и/или инъекцию:

// ** WARNING: Untested code

Оригинал

void funcToTest(String a, String b) {
  int i = new Random().nextInt(2);
  return ((i == 0) ? a : b);
}

Разделение

void funcToTest(String a, String b) {
  return funcToTest(a, b, (new Random().nextInt(2) == 0));
}
void funcToTest(String a, String b, boolean r) {
  return (r ? a : b);
}

Теперь проверка

funcToTest(String a, String b, boolean r).

Вы верите, что, если вы проверите вышеупомянутое, то

funcToTest(String a, String b)

будет работать просто работа.

Вливание

interface BoolRandomizer {
  bool getBool();
}
BoolRandomizer BOOL_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return (new Random().nextInt(2) == 0); }
}
BoolRandomizer ALWAYS_FALSE_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return false; }
}
BoolRandomizer ALWAYS_TRUE_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return true; }
}

class ClassUnderTest {
    private final BoolRandomizer br;
    ClassUnderTest() { this(BOOL_RANDOMIZER); }
    ClassUnderTest(BoolRandomizer br) { this.br = br; }
    void funcToTest(String a, String b) {
      return (br.getBool() ? a : b);
    }
}

Теперь проверьте

new ClassUnderTest(ALWAYS_FALSE_RANDOMIZER).funcToTest()

и

new ClassUnderTest(ALWAYS_TRUE_RANDOMIZER).funcToTest()

Конечно, есть более сложные/хитрые способы сделать это, но это основная идея. Я использовал эти приемы в программе для колоды карт, чтобы протестировать функцию тасовки. Другой случай: функцию, которая использует текущее время, вызывая системный API gettime(), может быть трудно протестировать, если только вы не готовы запускать модульный тест в очень определенное время суток :-). Разделение и инъекции облегчают тестирование.

1
ответ дан 1 December 2019 в 07:51
поделиться

Результат модульного тестирования - это не одно значение для данного ответа, это распределение значений для ряда входов. Вероятность означает, что вам придется провести много тестов, чтобы построить это распределение.

Если ваш объект действительно должен возвращать одно значение в среднем половину времени, а другое - все остальное время, то подходящим тестом будет проведение множества тестов и проверка того, что предельное поведение дает нужное вам распределение. Постройте соответствующее распределение результатов и убедитесь, что оно соответствует вашим ожиданиям.

3
ответ дан 1 December 2019 в 07:51
поделиться
Другие вопросы по тегам:

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