Проблема при модульном тестировании многопоточного кода - PullRequest
4 голосов
/ 03 января 2011

Я реализовал класс Pipe, который внутренне использует BlockingQueue для хранения полученных данных.

Существуют две ситуации, когда BlockingQueue блокирует вызывающий поток:

  1. Вызывающий поток звонит на Dequeue(), и очередь пуста.Он будет блокировать поток до тех пор, пока не найдется элемент для извлечения.
  2. Вызывающий поток выполняет вызов Enqueue(), и очередь заполняется.Он будет блокировать поток до тех пор, пока снова не останется места для вставки данных.

Моя первоначальная идея заключалась в том, чтобы вместо того, чтобы класс Pipe создавал экземпляр BlockingQueue, я бы пропустил экземплярIQueue к нему с помощью инжектора конструктора.Таким образом, при тестировании я передаю ему экземпляр NonBlockingQueue, поэтому мне не придется беспокоиться о проблемах с многопоточностью (когда я выполняю юнит-тесты как для класса Pipe, так и для другихклассы, которые используют Pipes Я хотел бы просто добавить вещи в очереди и не думать о том, заполнены ли они уже и тому подобное).

Беда в том, что при этом яфактически заставляя мой Pipe вести себя двумя совершенно разными способами, в зависимости от типа IQueue экземпляра, который я передаю ему:

  • В BlockingQueue, если очередьпусто, и вы пытаетесь извлечь что-то из него, оно будет блокироваться, пока не получит что-то.В NonBlockingQueue это просто вызовет исключение.

  • В BlockingQueue, если очередь заполнена и вы пытаетесь что-то добавить, она будет ждать, пока кто-то не выйдет из очередиэлемент, и снова есть место.Версия NonBlockingQueue либо выдаст FullQueueException, либо допустит «бесконечное» количество элементов.

То есть «единого контракта» не существует.Я думаю, что этот подход определенно неправильный.

Какой подход к этому более уместен?

Редактировать в Mitch:

Это используется для реализации системы Pipe & Filter: каждыйФильтр имеет входной и выходной патрубки.Каждый фильтр затем реализуется с помощью кода вида

char c;
while ((c = inputPipe.ReadChar()) != STREAM_TERMINATOR) { 
//I don't have to care 
//if right now there is any data. I know that if there isn't, 
//the thread will block and this will continue after there is some.

    ...do processing
    outputPipe.WriteChar(something);
}
outputPipe.WriteChar(STREAM_TERMINATOR);

, поэтому я предполагаю, что да, блокировка каналов / очередей - это то поведение, которое мне нужно.

Ответы [ 2 ]

1 голос
/ 03 января 2011

Ваша идея о внедрении в конструктор реализации IQueue верна.

Что вы хотите здесь провести модульным тестом?

  1. IQueue реализация
  2. Pipe ответственность (кроме взаимодействия с IQueue реализацией)
  3. Взаимодействие реализации Pipe и IQueue

Вы хотите сосредоточиться на точке # 3.Я думаю, что вам нужно подумать об ответственности здесь.Кто несет ответственность за то, чтобы очередь была заблокирована или нет.Это ответственность IQueue реализации, а не Pipe.Таким образом, в IQueue контракте будет просто «действие» (вызов метода), которое происходит, когда мы имеем исключительную ситуацию (удаление из пустой очереди или добавление в полную очередь).Вы хотите выполнить модульное тестирование этого взаимодействия, что означает просто проверку, вызывается ли метод действия в исключительной ситуации.

1 голос
/ 03 января 2011

Идея модульного тестирования заключается в том, что вы тестируете небольшие порции кода, и со многими из них вы в конечном итоге тестируете весь свой код. Есть причина, по которой он разбивается на части, тестировать по одному за раз проще, чем тестировать все одновременно. В коде вы используете BlockingQueue, а в тестировании вы используете NonBlockingQueue, который вводит все виды сложных аспектов, и это отрицает цель модульного тестирования ... В тестах вы должны упрощать, а не усложнять. Так почему бы просто не использовать там BlockingQueue? Вы утверждаете, что многопоточность может быть проблемой, но, по крайней мере, используйте простую реализацию IQueue, которая извне работает точно так же, как BlockingQueue, но без проблем с поточностью. В модульных тестах вы должны быть в состоянии предоставить экземпляр такого в IQueue, который не вызывает исключений. Например, вместо того, чтобы обычно генерировать исключение, поскольку очередь пуста, просто создайте новый элемент. Это разрешено в тестах ....

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