Дразнить читателя с Mockito

В настоящее время я отлаживаю довольно сложный алгоритм, который исправляет ошибки в битовом потоке. BitReader Интерфейс довольно прост, и основной метод чтения выглядит так:

/**
  Reads bits from the stream.
  @param length number of bits to read (<= 64)
  @return read bits in the least significant bits
*/
long read(int length) throws IOException;

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

BitStreamFixer fixer = new BitStreamFixer(input);
int word1 = fixer.readWord();
int word2 = fixer.readWord();
// possibly a loop here
assertEquals(VALID_WORD1, word1);
assertEquals(VALID_WORD2, word2);
// maybe a loop here too

Теперь BitStreamFixer класс принимает экземпляр BitReader, При модульном тестировании фиксатора мне явно нужен один такой экземпляр. А где взять? У меня есть два очевидных варианта: либо дать ему реальную реализацию BitReader или высмеивать это.

Первый вариант не очень привлекателен, потому что он создает зависимость от другого объекта, который не имеет ничего общего с тестируемым классом. Более того, это не так просто, потому что существующие BitReader Реализации читают потоки ввода формы, поэтому мне понадобится либо файл, либо каким-то образом подготовленный байтовый массив, что довольно утомительно.

Последний вариант выглядит лучше и соответствует обычному подходу модульного тестирования. Тем не менее, так как я даже не должен знать, какие аргументы приведёт исправитель read издеваться не легко. Мне придется идти с when(bitReader.read(anyInt())).thenAnswer(...) подход, реализующий пользовательский ответ, который создаст много бесполезной логики для ложной подачи тестируемого объекта правильными битами в кусках любого размера, который он запрашивает. Учитывая, что потоки битов, с которыми я работаю, имеют довольно сложную высокоуровневую структуру, это нелегко. И введение логики в модульных тестах также не пахнет хорошо.

Как вы думаете, есть ли другой вариант? Или, может быть, один из них может быть улучшен так, что я не замечаю?

1 ответ

Решение

Напишите, протестируйте и используйте понятный многоразовый помощник по тестированию.

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

Вы перечислили два варианта:

  • Используйте макет BitReaderгде у вас достаточно уверенности в прогнозировании взаимодействий вашей системы, чтобы вы могли настроить весь диалог "когда А, затем Б". Моккинг может быть довольно легким, если у вас есть небольшая поверхность API независимых методов, таких как уровень RPC, но имитация может быть очень сложной, когда у вас есть объект с состоянием и непредсказуемыми вызовами методов. Насмешка также полезна для детерминистически заглушенных недетерминированных систем, таких как внешние серверы или псевдослучайные источники, или систем, которые еще не существуют; ни один из них не подходит для вас.

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

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

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

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

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

@Test public void shouldFixBrokenStream() {
  BitReader bitReader = new StreamBitReader(BitWriter.create()
      .pushBits(16, 0x8080)
      .pushBits(12, 0x000)   // invalid 12-bit sequence
      .pushBits(16, 0x8080)
      .asByteArrayInputStream());
  BitStreamFixer fixer = new BitStreamFixer(bitReader);
  assertEquals(0x80808080, fixer.read(32));
}

/** Of course, you could skip the BitReader yourself, and just make a new one. */
@Test public void shouldFixBrokenStream_bitReader() {
  BitReader bitReader = new InMemoryBitReader();
  bitReader.pushBits(16, 0x8080);
  bitReader.pushBits(12, 0x000);   // invalid 12-bit sequence
  bitReader.pushBits(16, 0x8080);

  BitStreamFixer fixer = new BitStreamFixer(bitReader);
  assertEquals(0x80808080, fixer.read(32));
}

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

Другие вопросы по тегам