1. 소개

 

InputStream

은 데이터 처리에 사용되는 일반적인 추상 클래스입니다. 데이터는 매우 다른 소스에서 시작될 수 있지만 클래스를 사용하면 소스에서 추상화하고 특정 소스와 독립적으로 처리할 수 있습니다.그러나 테스트를 작성할 때 실제로 몇 가지 견고한 구현을 제공해야 합니다. 이 사용방법(예제)에서는 사용 가능한 구현 중 어떤 것을 선택해야 하는지 또는 언제 직접 작성하는 것이 더 좋은지 알아봅니다.

2. InputStream 인터페이스 기본

 
우리 자신의 코드를 작성하기 전에

InputStream

인터페이스가 어떻게 구축되는지 조금 이해하는 것이 좋습니다. 다행히도 매우 간단합니다.

간단한 InputStream 을 구현하려면 read

메소드 하나만 고려하면 됩니다 . 매개변수를 사용하지 않고 스트림의 다음 바이트를

int

로 반환합니다 .

InputStream

이 종료 되면 -1을 반환하여 처리를 중지하라는 신호를 보냅니다.

2.1. 테스트 케이스

 

이 예제에서는 InputStream

의 형태로 문자 메시지를 처리 하고 처리된 바이트 수를 반환하는 한 가지 방법을 테스트할 것입니다 . 그런 다음 올바른 바이트 수를 읽었는지 확인합니다.
int bytesCount = processInputStream(someInputStream);
assertThat(bytesCount).isEqualTo(expectedNumberOfBytes);

processInputStream()

메서드가 내부적으로 수행 하는 작업 은 여기서는 덜 관련성이 있으므로 매우 간단한 구현을 사용하고 있습니다.
public class MockingInputStreamUnitTest { 
    int processInputStream(InputStream inputStream) throws IOException {
        int count = 0;
        while(inputStream.read() != -1) {
            count++;
        }
        return count;
    }
}

2.2. 순진한 구현 사용

 

InputStream

의 작동 방식을 더 잘 이해하기 위해 하드코딩된 메시지로 간단한 구현을 작성합니다. 메시지와는 별도로 우리의 구현에는 다음에 읽어야 할 메시지의 바이트를 가리키는 인덱스가 있습니다. read 메서드가 호출될 때마다 메시지에서 1바이트를 가져온 다음 인덱스를 증가시킵니다.그렇게 하기 전에 메시지에서 모든 바이트를 아직 읽지 않았는지 확인해야 합니다. 그렇다면 -1을 반환해야 합니다.
public class MockingInputStreamUnitTest {

@Test
public void givenSimpleImplementation_shouldProcessInputStream() throws IOException {
    int byteCount = processInputStream(new InputStream() {
        private final byte[] msg = "Hello World".getBytes();
        private int index = 0;
        @Override
        public int read() {
            if (index >= msg.length) {
                return -1;
            }
            return msg[index++];
        }
    });
    assertThat(byteCount).isEqualTo(11);
}

3. ByteArrayInputStream 사용하기

 

전체 데이터 페이로드가 메모리에 맞는다고 절대적으로 확신하는 경우 가장 간단한 선택은 ByteArrayInputStream 입니다.

생성자에 바이트 배열을 제공한 다음 스트림은 이전 섹션의 예제와 유사한 방식으로 바이트 단위로 이를 반복합니다.
String msg = "Hello World";
int bytesCount = processInputStream(new ByteArrayInputStream(msg.getBytes()));
assertThat(bytesCount).isEqualTo(11);

4. FileInputStream 사용하기

 

데이터를 파일로 저장할 수 있다면 FileInputStream

형식으로 로드할 수도 있습니다 .

이 접근 방식의 장점은 데이터가 전체적으로 메모리에 로드되지 않고 필요할 때 디스크에서 읽는다는 것입니다.

파일을 리소스 폴더에 넣으면 편리한

getResourceAsStream 

메서드를 사용 하여 한 줄의 코드에 있는 경로에서 직접

InputStream 을 만들 수 있습니다.

InputStream inputStream = MockingInputStreamUnitTest.class.getResourceAsStream("/mockinginputstreams/msg.txt");
int bytesCount = processInputStream(inputStream);
assertThat(bytesCount).isEqualTo(11);
이 예에서

InputStream

의 실제 구현 은

BufferedFileInputStream

입니다. 이름에서 알 수 있듯이 더 큰 데이터 청크를 읽고 버퍼에 저장합니다. 따라서 디스크에서 읽기 횟수를 제한합니다.

5. 즉석에서 데이터 생성

 
때때로 우리는 시스템이 많은 양의 데이터로 제대로 작동하는지 테스트하고 싶습니다. 디스크에서 로드된 큰 파일을 사용할 수도 있지만 이 방법에는 몇 가지 심각한 단점이 있습니다. 잠재적인 공간 낭비일 뿐만 아니라

git

과 같은 버전 제어 시스템 은 큰 바이너리 파일과 잘 작동하도록 만들어지지 않았습니다. 다행히 미리 모든 데이터를 가질 필요는 없습니다. 대신, 즉시 생성할 수 있습니다.이를 달성하려면

InputStream

을 구현해야 합니다 . 필드와 생성자를 정의하는 것으로 시작하겠습니다.
public class GeneratingInputStream extends InputStream {
    private final int desiredSize;
    private final byte[] seed;
    private int actualSize = 0;

    public GeneratingInputStream(int desiredSize, String seed) {
        this.desiredSize = desiredSize;
        this.seed = seed.getBytes();
    }
}
"desiredSize" 변수는 언제 데이터 생성을 중지해야 하는지 알려줍니다. "seed" 변수는 반복될 데이터 덩어리가 됩니다. 마지막으로

"actualSize"

변수는 반환된 바이트 수를 추적하는 데 도움이 됩니다.

실제로 데이터를 저장하지 않기 때문에 필요합니다. "현재" 바이트만 반환합니다.

정의한 변수를 사용하여

read

메소드를 구현할 수 있습니다.
@Override
public int read() {
    if (actualSize >= desiredSize) {
        return -1;
    }
    return seed[actualSize++ % seed.length];
}
먼저 원하는 크기에 도달했는지 확인합니다. 그렇다면 스트림의 소비자가 읽기를 중지할 수 있도록 -1을 반환해야 합니다. 그렇지 않은 경우 시드에서 1바이트를 반환해야 합니다. 어떤 바이트여야 하는지 결정하기 위해

모듈로 연산자

를 사용하여 생성된 데이터의 실제 크기를 시드 길이로 나눈 나머지를 얻습니다.

6. 요약

 
이 사용방법(예제)에서는 테스트에서

InputStreams 를 처리하는 방법을 살펴보았습니다.

우리는 클래스가 어떻게 구축되고 다양한 시나리오에 어떤 구현을 사용할 수 있는지 배웠습니다. 마지막으로 데이터를 즉석에서 생성하기 위해 자체 구현을 작성하는 방법을 배웠습니다.항상 그렇듯이 코드 예제는

GitHub에서 사용할 수 있습니다.

Junit footer banner