Search code examples
javaunit-testingmockitojunit4

Unable to test a deserialization class using JUnit and Mockito


Purpose: To build a stickynote application using TDD (which I recently learned and now actively regretting)

Problem: I expect all the "Note"s to be serialized and deserialized by thier own individual classes. And I wish to use the TDD approach, but I am unable to even test the happy path of the NoteReader class (deserializer) let alone the corner cases.

Here is the Code:

package com.domainname.applicationname;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

public class NoteReader {
    private final FileInputStream fileInputStream;

    public NoteReader(FileInputStream fileInputStream) {
        this.fileInputStream = fileInputStream;
    }

    @SuppressWarnings("unchecked")
    public List<Note> load() {
        ObjectInputStream objectInputStream = null;
        List<Note> output = null;
        try {
            objectInputStream = new ObjectInputStream(fileInputStream);
            output = (List<Note>) objectInputStream.readObject();
            objectInputStream.close();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        return output;
    }
}

and here is the unit testing code:

package com.domainname.applicationname;

import org.junit.*;
import org.mockito.Mockito;

import java.io.*;
import java.util.Arrays;
import java.util.List;

public class NoteReaderTest {
    private FileInputStream dummyFileInputStream;
    private NoteReader noteReaderDummy;

    private List<Note> expectedOutput = Arrays.asList(
            new Note("some written text"),
            new Note("some other written text", NoteColor.lightGreen)
    );

    private ByteArrayOutputStream byteArrayOutputStream;
    private ObjectOutputStream objectOutputStream;
    private byte[] bytesToBeDeserialized;

    @Before
    public void setUp() throws IOException {
        dummyFileInputStream = Mockito.mock(FileInputStream.class);
        noteReaderDummy = new NoteReader(dummyFileInputStream);

        byteArrayOutputStream = new ByteArrayOutputStream();
        objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    }

    @After
    public void tearDown() throws IOException {
        noteReaderDummy = null;
        byteArrayOutputStream.flush();
        objectOutputStream.flush();
        objectOutputStream.close();
    }

    @Test
    public void shouldLoadTheListOfNotes() throws IOException {
        //given
        objectOutputStream.writeObject(expectedOutput);
        bytesToBeDeserialized = byteArrayOutputStream.toByteArray();
        int intValueOfByteArray = dummyFileInputStream.read(bytesToBeDeserialized);
        //when
        Mockito.when(
                dummyFileInputStream.read()
        ).thenReturn(
                intValueOfByteArray
        );

        //then
        Assert.assertEquals(
                "the notes have not been loaded",
                expectedOutput,
                noteReaderDummy.load()
        );
    }
}

This has b/me an infinite loop and it's driving me nuts.

Question: How do I test a deserialization class? What am I doing wrong in the above code?


Solution

  • For anybody else that might face the same issue, override the hashCode() and equals() method of the Note class to work like I intend it to. Instantiate the List to ArrayList or LinkedList. This solved it for me. The one thing I couldn't figure out is how to use Mockito with it. So I took the advice of @PhilNinan and use a TemporaryFolder with a tempFile. Then serialized into that file and read that file (not ideal but couldn't find another way). The NoteReader:

    package com.somedomainname.someapplication;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    class NoteReader {
        private final FileInputStream fileInputStream;
    
        NoteReader(FileInputStream fileInputStream) {
            this.fileInputStream = fileInputStream;
        }
    
        @SuppressWarnings("unchecked")
        List<Note> load() throws IOException {
            ObjectInputStream objectInputStream = null;
            List<Note> output = new ArrayList<>();
    
            try {
                objectInputStream = new ObjectInputStream(fileInputStream);
                while (fileInputStream.available() != 0) {
                    Note n = (Note) objectInputStream.readObject();
                    output.add(n);
                }
    
            } catch (ClassNotFoundException | IOException e) {
                fileInputStream.close();
                assert objectInputStream != null;
                objectInputStream.close();
    
                e.printStackTrace();
            }
            return output;
        }
    
    }
    

    The NoteReaderTest class:

    package com.somedomainname.someapplicationname;
    
    import org.junit.*;
    import org.junit.rules.TemporaryFolder;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class NoteReaderTest {
        private NoteReader noteReaderDummy;
    
        private ObjectOutputStream objectOutputStream;
    
        @Rule
        public TemporaryFolder temporaryFolder = new TemporaryFolder();
    
        private File tempFile;
    
        private static List<Note> inputList = new ArrayList<>();
        static {
            inputList.add(new Note("some written text"));
            inputList.add(new Note("some other written text", NoteColor.lightGreen));
        }
    
        private static List<Note> anotherList = new ArrayList<>();
        static {
            anotherList.add(new Note("qazwsxedcrfv"));
            anotherList.add(new Note("qwertyuiopasdfghjkl", NoteColor.lightRed));
        }
    
        @Before
        public void setUp() throws IOException {
            tempFile = temporaryFolder.newFile("someBullshit.ser");
            objectOutputStream = new ObjectOutputStream(new FileOutputStream(tempFile));
    
            for(Note n : inputList) {
                objectOutputStream.writeObject(n);
            }
    
            noteReaderDummy = new NoteReader(new FileInputStream(tempFile));
    
        }
    
        @After
        public void tearDown() throws IOException {
            objectOutputStream.flush();
            objectOutputStream.close();
            tempFile = null;
            temporaryFolder = null;
            noteReaderDummy = null;
    
        }
    
        /**
         * This test method tests the happy path of the NoteReader.load() method.
         * @throws IOException
         */
    
        @Test
        public void shouldLoadTheListOfNotes() throws IOException {
            //given
    
            //then
            List<Note> output = noteReaderDummy.load();
            Assert.assertEquals(
                    "the notes have not been loaded",
                    inputList,
                    output
            );
    
        }
    
        /**
         * This test method is responsible for confirming that the output of the
         * NoteReader.load() method doesn't stray from the expected one.
         * @throws IOException
         */
    
        @Test
        public void shouldNotLoadTheOtherListOfNotes() throws IOException {
            //given
    
            //then
            List<Note> output = noteReaderDummy.load();
            Assert.assertNotEquals(
                    "it loaded the wrong fucking list",
                    anotherList,
                    output
            );
    
        }
    
        /**
         * this test method is responsible for checking that the output of
         * the method NoteReader.load() is not null
         * @throws IOException
         */
    
        @Test
        public void shouldNotBeNull() throws IOException {
            //given
    
            //then
            List<Note> output = noteReaderDummy.load();
            Assert.assertNotNull(
                    "fuck it's null",
                    output
            );
        }
    }