Search code examples
javaunit-testing

Why am I having java.io.IOException: Stream Closed in my test?


While working on the UT of a java 11 with Springboot project I have found the following stacktrace:

java.io.IOException: Stream Closed
    at java.base/java.io.FileInputStream.readBytes(Native Method)
    at java.base/java.io.FileInputStream.read(FileInputStream.java:279)
    at com.amazonaws.internal.SdkFilterInputStream.read(SdkFilterInputStream.java:90)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
    at 

I have tried to create a minimal reproducible example:


@Test
    public void minimalExampleTest() throws IOException {
        when(s3Service.getObject(Mockito.anyString())).thenReturn(getS3Object("image.jpg"));

        try(FileOutputStream fos = new FileOutputStream( "src/test/resources/exported_data_test.zip");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ZipOutputStream zos = new ZipOutputStream(bos)){
            service.minimalExample(zos, "directory/s3");
        }

        ZipFile zipFile = new ZipFile( "src/test/resources/exported_data_test.zip");
        List<String> entries = zipFile.stream().map(ZipEntry::getName).collect(Collectors.toList());

        //assert
        assertNotNull(entries);
        assertTrue(entries.stream().filter(ent -> ent.contains(".jpg")).collect(Collectors.toList()).size() == 4);
        List<String> imagesNameList = List.of("doc1_export.jpg", "doc2_export.jpg", "doc3_export.jpg", "doc4_export.jpg");
        entries.stream().forEach(imageName -> assertTrue(imagesNameList.contains(imageName)));

        //generated files deletion
        entries.stream().forEach(imageName -> {
            File file = new File("src/test/resources" + "/" + imageName);
            file.delete();
        });
        File file = new File( "src/test/resources/exported_data_test.zip");
        file.delete();
    }

    private static S3Object getS3Object(String fileName) throws FileNotFoundException {
        S3Object s3Object = new S3Object();
        s3Object.setKey(fileName);
        File initialFile = new File(IMAGES_DOSSIER + "/" + fileName);
        InputStream targetStream = new FileInputStream(initialFile);
        s3Object.setObjectContent(targetStream);
        return s3Object;
    }
 public ZipOutputStream minimalExample (ZipOutputStream zipOutputStream, String directoryName) {
        try {
            List<String> listDocumentType = List.of("doc1", "doc2", "doc3", "doc4");
            Map<String, S3ObjectInputStream> s3ObjectInputStreamList = new HashMap<>();
            listDocumentType.stream().forEach(docType -> {
                String fileName = directoryName + '/' + docType + "_export.jpg";
                S3Object s3Object = s3Service.getObject(fileName);
                if (s3Object != null)
                    s3ObjectInputStreamList.put(docType + "_export.jpg", s3Object.getObjectContent());
            });

            s3ObjectInputStreamList.keySet().forEach(key -> {
                var zipEntry = new ZipEntry(key);
                try {
                    zipOutputStream.putNextEntry(zipEntry);
                    var objectContent = s3ObjectInputStreamList.get(key);
                    var bytes = new byte[1024];
                    int length;
                    while ((length = objectContent.read(bytes)) >= 0) {
                        zipOutputStream.write(bytes, 0, length);
                    }
                    objectContent.close();
                    zipEntry.clone();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            zipOutputStream.closeEntry();
            zipOutputStream.close();
        } catch (IOException e) {
            logger.log(Level.LOW, e.getMessage());
        }
        return zipOutputStream;
    }

The case is that the code when used in the application it works well, but in the test it does the first iteration of the while and in the second iteration the error shown in the stack trace pops. (In the minimal example the test pass - the error is not blocking)

Error Stream Closed

I don't know why I'm having the error java.io.IOException: Stream Closed. If someone could please give an explanation/solution to this problem?


Solution

  • The problem was that we were calling to the method in S3Service 4 times and returning the same image so when we tried to read the object content there was a stream already created.

    The problem has been fiched pointing the returns of the mock to different files, as in the code below:

    @Test
        public void minimalExampleTest() throws IOException {
            when(s3Service.getObject(Mockito.anyString())).thenReturn(getS3Object("image.jpg"))
    .thenReturn(getS3Object("image1.jpg"))
    .thenReturn(getS3Object("image2.jpg"))
    .thenReturn(getS3Object("image3.jpg"));
    
            try(FileOutputStream fos = new FileOutputStream( "src/test/resources/exported_data_test.zip");
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                ZipOutputStream zos = new ZipOutputStream(bos)){
                service.minimalExample(zos, "directory/s3");
            }
    
            ZipFile zipFile = new ZipFile( "src/test/resources/exported_data_test.zip");
            List<String> entries = zipFile.stream().map(ZipEntry::getName).collect(Collectors.toList());
    
            //assert
            assertNotNull(entries);
            assertTrue(entries.stream().filter(ent -> ent.contains(".jpg")).collect(Collectors.toList()).size() == 4);
            List<String> imagesNameList = List.of("doc1_export.jpg", "doc2_export.jpg", "doc3_export.jpg", "doc4_export.jpg");
            entries.stream().forEach(imageName -> assertTrue(imagesNameList.contains(imageName)));
    
            //generated files deletion
            entries.stream().forEach(imageName -> {
                File file = new File("src/test/resources" + "/" + imageName);
                file.delete();
            });
            File file = new File( "src/test/resources/exported_data_test.zip");
            file.delete();
        }