导致 IOException 覆盖 catch 块

Cause IOException to cover catch blocks

我试图覆盖下面代码中的 catch 块,但我做不到。我知道我需要在将 reading/writing 放入文件时引发 IOException,但我做不到。另外,我不能使用 PowerMockito 来覆盖静态方法,因为我正在使用 Junit5。有人可以帮忙吗?

 @Override
    public String execute(UploadCategoryImageCommandRequest request) {
        File tempFile = null;
        FileOutputStream fos = null;
        String fileName = "";
            fileName = UUID.randomUUID().toString() + System.currentTimeMillis() + ".svg";
            try {
                tempFile = File.createTempFile(fileName, "svg");
                fos = new FileOutputStream(tempFile);
                fos.write(request.getFile().getBytes());
            } catch (IOException e) {
                log.error("File Creation Error: {}", e);
                upload.setFileStatus(FileStatus.FAILED);
                uploadRepository.save(upload);
                throw new ApplicationException(ErrorKey.IMAGE_UPLOAD, ErrorCode.FAILED);
            }
                return string;
            
        }

这是我编写的测试用例之一,但是 returns 这个错误 - org.mockito.exceptions.misusing.MissingMethodInvocationException: when() 需要一个必须为 'a method call on a mock' 的参数。 例如: 当(mock.getArticles()).thenReturn(文章);

此外,我不确定是否应该在我编写的以下测试用例中模拟文件 class

 @Test
    public void exception() throws IOException {
        Upload upload = Upload.builder().fileStatus(FileStatus.FAILED).imagePath("http://").build();
        MockMultipartFile firstFile = new MockMultipartFile("fileName", "fileName.svg", "text/plain", "some xml".getBytes());
        UploadCategoryImageCommandRequest uploadCategoryImageCommandRequest = UploadCategoryImageCommandRequest.builder()
                .file(firstFile)
                .uploadType(UploadType.CATEGORY)
                .build();     
Mockito.when(file.createTempFile("filename","svg")).thenThrow(IOException.class);
        uploadCategoryImageCommand.execute(uploadCategoryImageCommandRequest);

    }

静态访问始终是一个问题,不仅在测试上下文中,而且变得最明显。

一种解决方案是将静态访问置于非静态方法中的单独 class 中。这个 class 将“太简单而不会失败”,因此没有代码覆盖率是合理的。将此助手 class 的一个实例作为构造函数参数注入到您的 待测代码 中。然后你可以简单地用 mock 替换它,在那个特定的测试中抛出所需的异常。


Can you explain this with an example? - Meg

新的 class(不需要单元测试 -> 覆盖率 0%)

    class TempFileCreator{
      File createTempFile(String baseName, String extension){
         return File.createTempFile(fileName, extension);
      }
    }

被测代码的更改 (CUT):

    class YourClass{
       private final TempFileCreator  tempFileCreator;

       YourClass(TempFileCreator  tempFileCreator 
            /* more constructor parameters? */
            /* update the Builder, may hold an instance of TempFileCreator as a constant */
       ){
         this.tempFileCreator = tempFileCreator;
       }

       @Override
        public Single<String> execute(UploadCategoryImageCommandRequest request) {
          // lots of your code
            try {
              // more of your code
              File tempfile = tempFileCreator.createTempFile(fileName, "svg");
              // tempfile.deleteOnExit(); would prevent the extra section in finally block...
              // more of your code          
            } catch (IOException e) {
              // the code to verify (coverage does not have a value as such...)
            }
          }
    }

你的测试:

@Mock
TempFileCreator  tempFileCreator; // should be injected to your CUT like the other mocks...

@Test
public void exception() throws IOException {
   Mockito.doThrow(new IOException("this is a test")).when(tempFileCreator).createTempFile(anyString(),anyString());
   // prefer this form since it does not execute the mocked method while the when().then*() form does...

   // the rest of yout test code
}

File.createTempFile 在默认临时目录中创建一个文件,您可以通过设置 java.io.tmpdir 系统 属性 来覆盖它。如果你将它设置到某个不存在的目录,临时文件创建将失败,你会得到一个 IOException。只要确保在完成后恢复它,就不会搞乱其余的测试。 JUnit Pioneer 的 SetSystemProperty 提供了一种优雅的方式来做到这一点:

@Test
@SetSystemProperty(key = "java.io.tmpdir", value = "/no/such/dir")
public void exception() throws IOException {
    // Call your code
}