Всегда блокировать входной поток для тестирования? - PullRequest
4 голосов
/ 07 июля 2011

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

InputStream in = new ByteArrayInputStream("".getBytes());

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

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

Кто-нибудь знает лучшее решение?


РЕДАКТИРОВАТЬ:

Это моя новая попытка.Он работает большую часть времени, но в других случаях входной поток умирает рано, что приводит к смерти выходного потока (такое поведение является преднамеренным).Хотя я не могу понять, почему иногда это не получается.

Это общий тест для TestNG, упрощенный для ясности.

    protected CountDownLatch inputLatch;

    @BeforeMethod
    public void botSetup() throws Exception {
            //Setup streams for bot
            PipedOutputStream out = new PipedOutputStream();
            //Create an input stream that we'll kill later
            inputLatch = new CountDownLatch(1);
            in = new AutoCloseInputStream(new ByteArrayInputStream("".getBytes()) {
                    @Override
                    public synchronized int read() {
                            try {
                                    //Block until were killed
                                    inputLatch.await();
                            } catch (InterruptedException ex) {
                                    //Wrap in an RuntimeException so whatever was using this fails
                                    throw new RuntimeException("Interrupted while waiting for input", ex);
                            }
                            //No more input
                            return -1;
                    }
            });
            Socket socket = mock(Socket.class);
            when(socket.getInputStream()).thenReturn(in);
            when(socket.getOutputStream()).thenReturn(out);

            //Setup ability to read from bots output
            botOut = new BufferedReader(new InputStreamReader(new PipedInputStream(out)));
            ...
    }

    @AfterMethod
    public void cleanUp() {
            inputLatch.countDown();
            bot.dispose();
    }

Для теста я использую readLine() из botOut, чтобы получить соответствующее количество строк.Проблема заключается в том, что когда выходной поток умирает, readLine() блокируется навсегда, что приводит к зависанию TestNG.Я пробовал тайм-аут со смешанными результатами: большую часть времени он работал бы, но в других он убивал бы тесты, для тестирования которых потребовалось немного больше времени, чем обычно.

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

Ответы [ 5 ]

2 голосов
/ 07 июля 2011

Я бы сделал InputStream, который, когда read (), выполняет wait () для чего-то, что удерживается заблокированным, пока вы не закончите с остальной частью теста.Подкласс от FilterInputStream, чтобы получить все остальное бесплатно.

2 голосов
/ 07 июля 2011

Mockito великолепен - я лично большой фанат!

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

import static org.mockito.Mockito.*;

//...
@Test
public void testMockitoSleepOnInputStreamRead() throws Exception{

    InputStream is = mock(InputStream.class);
    when(is.read()).thenAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) {
            try {
            Thread.sleep(10000000000L);
            return null;
            } catch (InterruptedException ie) {
                throw new RuntimeException(ie);
            }

        }
    });

    //then use this input stream for your testing.
}
1 голос
/ 29 июля 2011

Кажется, нет надежного способа сделать это. Мой код в вопросе работает только иногда, @ Moe вообще не работает, @ Ed подсказывает, что я делал изначально, а @ SJuan's вроде того, что я уже делаю.

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

Чтобы исправить проблему, я сделал то, что должен был сделать изначально: создаю фабричный метод для InputThread (потока, который фактически выполняет чтение), затем переопределяю в моем тестировании. Простой, эффективный и надежный на 100%.

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

0 голосов
/ 29 июля 2016

Я создал вспомогательный класс, который расширяет ByteArrayInputStream для моих модульных тестов.Он передает данный byte[] через, но в конце потока вместо возврата -1 он ждет, пока не будет вызван close().Если прошло десять секунд, он сдается и выдает исключение.

Если вы хотите, чтобы он закрылся раньше, вы можете позвонить latch.countdown() самостоятельно.

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class BlockingByteArrayInputStream extends ByteArrayInputStream {
    private CountDownLatch latch;

    public BlockingByteArrayInputStream(byte[] buf) {
        super(buf);
        latch = new CountDownLatch(1);
    }

    @Override
    public synchronized int read() {
        int read = super.read();
        if (read == -1) {
            waitForUnblock();
        }
        return read;
    }

    @Override
    public int read(byte[] b) throws IOException {
        int read = super.read(b);
        if (read == -1) {
            waitForUnblock();
        }
        return read;
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) {
        int read = super.read(b, off, len);
        if (read == -1) {
            waitForUnblock();
        }
        return read;
    }

    private void waitForUnblock() {
        try {
            latch.await(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException("safeAwait interrupted");
        }
    }

    @Override
    public void close() throws IOException {
        super.close();
        latch.countDown();
    }
}
0 голосов
/ 07 июля 2011

Тогда вам нужен еще один аромат InputStream. Читать блок, когда больше нет доступных байтов, но с ByteArrayOutputStream они всегда доступны, пока не будет найден конец потока.

Я бы расширил BAOS, изменив read (), чтобы он проверял определенное логическое значение (если true read, если false, подождите секунду и выполните цикл). Затем измените эту переменную из кода вашего устройства, когда наступит подходящее время.

Надеюсь, что поможет

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