Search code examples
javamockitojunit4printwriterpowermockito

Wanted but not invoke: Mockito PrintWriter


Hi I am working on a project and using PrintWriter class for opening and writing in the file. But when I am writing the test case for same it gives following error at Line 153

Wanted but not invoked:
  mockPrintWriter.println("ID    url1
  ");
 -> at x.y.z.verify(ProcessImageDataTest.java:153)
 Actually, there were zero interactions with this mock.

Code: (Uses Lombok Library)

ProcessImageData.java

@Setter
@RequiredArgsConstructor
public class ProcessImageData implements T {
    private final File newImageDataTextFile;

   @Override
    public void execute() {
       LineIterator inputFileIterator = null;
       try {
        File filteredImageDataTextFile = new File(filteredImageDataTextFilepath);

          PrintWriter writer = new PrintWriter(newImageDataTextFile);
          inputFileIterator = FileUtils.lineIterator(filteredImageDataTextFile, StandardCharsets.UTF_8.displayName());
          while (inputFileIterator.hasNext()) {
                    if(someCondition)
           **Line51**           writer.println(imageDataFileLine);
                    //FileUtils.writeStringToFile(newImageDataTextFile, imageDataFileLine + NEWLINE, true);
                }
            }          

      } catch (Exception e) {

      } finally {
          LineIterator.closeQuietly(inputFileIterator);
 **LINE63**         writer.close();
      }
  }

ProcessImageDataTest.java

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ProcessImageData.class, FileUtils.class, Printwriter.class })
public class ProcessImageDataTest {

private ProcessImageData processImageData;

private static final String FILTERED_IMAGE_DATA_TEXT_FILE_PATH = "filteredFilepath";
private File FILTEREDFILE = new File(FILTERED_PATH);
private static final File IMAGE__FILE = new File("imageFilePath");

private LineIterator lineIterator;
@Mock
private PrintWriter mockPrintWriter;

@Before
public void init() throws Exception {
    MockitoAnnotations.initMocks(this);

    processImageData = new ProcessImageData(Palettes_file, FILTERED_PATH, IMAGE_FILE);

    PowerMockito.mockStatic(FileUtils.class);
    PowerMockito.whenNew(PrintWriter.class).withArguments(IMAGE_FILE).thenReturn(mockPrintWriter);

    PowerMockito.when(FileUtils.lineIterator(FILTERED_FILE, StandardCharsets.UTF_8.displayName())).thenReturn(lineIterator);
    PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);

}

@Test
public void testTaskWhenIDInDBAndStale() throws IOException {
    PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID1 + SPACE + URL2);

    processImageData.execute();

    List<String> exepctedFileContentOutput = Arrays.asList(ID2 + SPACE + URL1 + NEWLINE);
    verify(exepctedFileContentOutput, 1, 1);
}


@Test
public void testTaskWhenIDNotInDB() throws IOException {
    PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID3 + SPACE + URL2);

    processImageData.execute();

    List<String> exepctedFileContentOutput = Arrays.asList(ID3 + SPACE + URL2 + NEWLINE);
    verify(exepctedFileContentOutput, 1, 1);
}

private void verify(List<String> exepctedFileContentOutput, int fileWriteTimes, int fileReadTimes) throws IOException {
    for (String line : exepctedFileContentOutput){
**Line153**                     Mockito.verify(mockPrintWriter,  Mockito.times(fileWriteTimes)).print(line);  
    }

    PowerMockito.verifyStatic(Mockito.times(fileReadTimes));
    FileUtils.lineIterator(FILTERED_IMAGE_DATA_TEXT_FILE, StandardCharsets.UTF_8.displayName());
}

}

I am mocking a new operator for PrintWriter also, injecting using beans. What is the mistake I am doing?? I am stuck on it from long time and not getting the error? Any help is appreciated.

Updated :

I did changes suggested below and updated the code, but now I get the error:

Wanted but not invoked: mockPrintWriter.print("ASIN2 url1 "); -> 
at softlines.ctl.ruleExecutor.tasks.ProcessImageDataTest.verify‌​(ProcessImageDataTes‌​t.java:153) 
However, there were other interactions with this mock: -> at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(Pr‌​ocessImageData.java:‌​51) ->  
at  softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(Pr‌​ocessImageData.java:‌​51) -> 
at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(Pr‌​ocessImageData.java:‌​58) –

Solution

  • I see 3 issues in your test:

    1. You don't try to mock the correct constructor, indeed in the method execute, you create your PrintWriter with only one argument of type File while you try to mock the constructor with 2 arguments one of type File and the other one of type String.

    So the code should rather be:

    PowerMockito.whenNew(PrintWriter.class)
        .withArguments(IMAGE_FILE)
        .thenReturn(mockPrintWriter);
    
    1. To be able to mock a constructor you need to prepare the class creating the instance which is ProcessImageData in this case, so you need to add ProcessImageData.class in the annotation @PrepareForTest. (I'm not sure ProcessImageDataTest.class is needed there)

    2. The field lineIterator should be annotated with @Mock.

    3. Instead of verifying print with a new line, you should verify directly println without new line it is much less error prone.


    I simplified your code to show the idea.

    Assuming that ProcessImageData is:

    public class ProcessImageData {
    
        private final File newImageDataTextFile;
    
        public ProcessImageData(final File newImageDataTextFile) {
            this.newImageDataTextFile = newImageDataTextFile;
        }
    
        public void execute() throws Exception{
            try (PrintWriter writer = new PrintWriter(newImageDataTextFile)) {
                LineIterator inputFileIterator = FileUtils.lineIterator(
                    newImageDataTextFile, StandardCharsets.UTF_8.displayName()
                 );
                while (inputFileIterator.hasNext()) {
                    writer.println(inputFileIterator.nextLine());
                }
            }
        }
    }
    

    My unit test would then be:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ProcessImageData.class, FileUtils.class})
    public class ProcessImageDataTest {
    
        private File file = new File("imageFilePath");
    
        private ProcessImageData processImageData;
        @Mock
        private PrintWriter mockPrintWriter;
        @Mock
        private LineIterator lineIterator;
    
        @Before
        public void init() throws Exception {
            MockitoAnnotations.initMocks(this);
    
            processImageData = new ProcessImageData(file);
            PowerMockito.whenNew(PrintWriter.class)
                .withArguments(file)
                .thenReturn(mockPrintWriter);
            PowerMockito.mockStatic(FileUtils.class);
            PowerMockito.when(
                FileUtils.lineIterator(file, StandardCharsets.UTF_8.displayName())
            ).thenReturn(lineIterator);
            PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);
        }
    
        @Test
        public void testExecute() throws Exception {
            PowerMockito.when(lineIterator.nextLine()).thenReturn("Foo", "Bar");
            processImageData.execute();
            Mockito.verify(mockPrintWriter,  Mockito.times(1)).println("Foo");
            Mockito.verify(mockPrintWriter,  Mockito.times(1)).println("Bar");
        }
    }
    

    For more details please refer to How to mock construction of new objects.


    how can I add verification in unit test for writer.close?

    One way could be to simply check that close() at be called once by adding the next line to your unit test:

    Mockito.verify(mockPrintWriter,  Mockito.times(1)).close();