Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test this file-writing, 3lines function?

This is my method in some service class. It's public so it should be tested. I simply do not know WHAT should I test. I'd mock Writer and spyOn function call, but with this implementation it's impossible (isn't it?)

I'm using Mockito and JUnit

For now, I can only make function to throw and assert that exception

Any help?

@Override
public void initIndexFile(File emptyIndexFile) {
    try {
        Writer writer = new FileWriter(emptyIndexFile);
        writer.write("[]");
        writer.close();
    } catch (IOException e) {
        throw new IndexFileInitializationException(
            "Error initialization index file " + emptyIndexFile.getPath()
        );
    }
}
like image 416
Konrad Dziurdź Avatar asked Oct 19 '25 12:10

Konrad Dziurdź


1 Answers

If you feel that adding the the special content is the business logic and therefore the responsibility of your class, then creating the FileWriter is not (according to the single responsibility pattern.

So you should use a FileWriterFactory that is injected into your Class under Test. Then you can mock that FileWriterFactory to return a mock implementation of the Writer interface on which in turn you can check that it got the expected String.

Your CuT would change to this:

private final WriterFactory writerFactory;

public ClassUnderTest(@Inject WriterFactory writerFactory){
   this.writerFactory = writerFactory;
}

@Override
public void initIndexFile(File emptyIndexFile) {
    try {
        Writer writer = writerFactory.create(emptyIndexFile);
        writer.write("[]");
        writer.close();
    } catch (IOException e) {
        throw new IndexFileInitializationException(
            "Error initialization index file " + emptyIndexFile.getPath()
        );
    }
}

and your test to this:

class Test{

  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 

  @Mock
  private FileWriterFactory fileWriterFactory;
  private Writer fileWriter = spy(new StringWriter());
  File anyValidFile = new File(".");
  @Test
  public void initIndexFile_validFile_addsEmptyraces(){
     //arrange
     doReturn(fileWriter).when(fileWriterFactory).create(any(File.class));

     // act
     new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);

     //assert
     verify(fileWriterFactory)create(anyValidFile);
     assertEquals("text written to File", "[]", fileWriter.toString());
     verify(fileWriter).close();
  }
}

in addition you could easily check that your CuT intercepts the IOException:

  @Rule
  public ExpectedException exception = ExpectedException.none();

  @Test
  public void initIndexFile_missingFile_IndexFileInitializationException(){
     //arrange
     doReturnThrow(new IOException("UnitTest")).when(fileWriterFactory).create(any(File.class));

     //assert
     exception.expect(IndexFileInitializationException.class);
     exception.expectMessage("Error initialization index file "+anyValidFile.getPath());

     // act
     new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
  }

Nice! a factory just to test 3 lines of code! – Nicolas Filotto

This is a good point.

The question is: will there be any method within that class ever interacting with the File object directly and needs to create the FileWriter afterwards?

If the answer is "no" (as it is most likely) following the KISS principle you should inject a Writer object directly instead of the factory and have your methods without the File parameter.

private final Writer writer;

public ClassUnderTest(@Inject Writer writer){
   this.writer = writer;
}

@Override
public void initIndexFile() {
    try {
        writer.write("[]");
        writer.close();
    } catch (IOException e) {
        throw new IndexFileInitializationException(
            "Error initialization index file " + emptyIndexFile.getPath()
        );
    }
}

modified test:

class Test{       
  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 
  @Rule public ExpectedException exception = ExpectedException.none();

  @Mock
  private FileWriterFactory fileWriterFactory;
  @Mock
  private Writer failingFileWriter;
  private Writer validFileWriter = spy(new StringWriter());
  File anyValidFile = new File(".");
  @Test
  public void initIndexFile_validFile_addsEmptyraces(){
     //arrange         
     // act
     new ClassUnderTest(validFileWriter).initIndexFile();

     //assert
     verify(fileWriterFactory)create(anyValidFile);
     assertEquals("text written to File", "[]", fileWriter.toString());
     verify(fileWriter).close();
  }

  @Test
  public void initIndexFile_missingFile_IndexFileInitializationException(){
     //arrange
     doReturnThrow(new IOException("UnitTest")).when(failingFileWriter).write(anyString());

     //assert
     exception.expect(IndexFileInitializationException.class);
     exception.expectMessage("Error initialization index file "+anyValidFile.getPath());

     // act
     new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
  }
}
like image 159
Timothy Truckle Avatar answered Oct 21 '25 03:10

Timothy Truckle



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!