【问题标题】:Force IOException during file reading在文件读取期间强制 IOException
【发布时间】:2020-03-30 11:52:03
【问题描述】:

我有一段从文件中读取数据的代码。我想在此代码中强制执行 IOException 以进行测试(在这种情况下,我想检查代码是否引发了正确的自定义异常)。

例如,有什么方法可以创建一个防止被读取的文件?也许处理一些安全检查会有所帮助?

请注意,传递不存在文件的名称无济于事,因为 FileNotFoundException 有一个单独的 catch 子句。

为了更好地理解问题,这是一段代码:

    BufferedReader reader = null;
    try {

        reader = new BufferedReader(new FileReader(csvFile));

        String rawLine;
        while ((rawLine = reader.readLine()) != null) {
            // some work is done here
        }

    } catch (FileNotFoundException e) {
        throw new SomeCustomException();
    } catch (IOException e) {
        throw new SomeCustomException();
    } finally {
        // close the input stream
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                // ignore
            }
        }
    }

【问题讨论】:

  • 您可以尝试读取不允许读取的文件。在 Linux 上,/proc/kcore 是一个不错的选择,/etc/shadow 也是如此。
  • 不是答案,而是观察。您应该使用自定义异常包装 IOException 以保留完整的堆栈跟踪。即throw new SomeCusomException(e)

标签: java junit ioexception


【解决方案1】:

免责声明:我没有在非 Windows 平台上测试过,所以在具有不同文件锁定特性的平台上可能会有不同的结果。

如果你事先锁定了文件,你可以在尝试读取文件时触发 IOException:

java.io.IOException: The process cannot access the file because another process has locked a portion of the file

即使您在同一个线程中也可以使用。

这里有一些示例代码:

final RandomAccessFile raFile = new RandomAccessFile(csvFile, "rw");
raFile.getChannel().lock();

【讨论】:

  • 对我不起作用 - 使用 Ubuntu .. 但这个想法很好:)
  • POSIX (ubuntu) 上的文件锁定是建议性的。在 Windows 上,它们是强制性的。因此,这仅适用于 Windows。
【解决方案2】:

如果您可以稍微重构代码以接受 Reader 而不是文件名,则可以使用模拟。使用EasyMock,您可以创建一个模拟阅读器,并将其设置为在调用您希望的任何方法时抛出 IOException。然后你只需将它传递给要测试的方法,然后观察会发生什么:-)

void readFile(Reader reader) throws SomeCustomException {
    try {
        String rawLine;
        while ((rawLine = reader.readLine()) != null) {
            // some work is done here
        }

    } catch (FileNotFoundException e) {
        throw new SomeCustomException();
    } catch (IOException e) {
        throw new SomeCustomException();
    } finally {
        // close the input stream
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                // ignore
            }
        }
    }
}

然后是测试代码:

mockReader = createMock(Reader.class);
expect(mockReader.readLine()).andThrow(
        new IOException("Something terrible happened"));
replay(mockReader);

objectToTest.readFile(reader);

【讨论】:

  • @DixonD:如果你不能改变它,而且它的设计不正确,那么你就不能对它进行单元测试。最好的办法就是进行一些集成测试。
【解决方案3】:

在您的测试中定义一个引发异常的重载 fileInputStream

FileInputStream s;
try {
    s = new FileInputStream(fileName) {

           @Override
           public int read() throws IOException {
              throw new IOException("Expected as a test");
           }
       };
} catch (FileNotFoundException e) {
   throw new RuntimeException(e.getMessage(), e);
}

【讨论】:

    【解决方案4】:

    聚会迟到了,但我找到了一种强制 IOException 的方法。我正在使用 BufferedWriter/Reader 保存/读取文本文件,并且正在执行如下所示的测试:

    public void testIOException() {
        try {
            int highScore = loadHighScore("/");
            fail("IOException should have been thrown");
        } catch (MissingFileException e) {
            fail("IOException should have been thrown");
        } catch (IOException e) {
            // expected
        }
    }
    

    其中“/”是文件名。在 loadHighScore 里面有一行:

    BufferedWriter bw = new BufferedWriter(new FileWriter(file));
    

    因为我的测试输入“file”为“/”,所以输出了一个IOException

    【讨论】:

      【解决方案5】:

      您可以尝试以超级用户身份创建文件,然后以标准用户身份阅读。那里应该有权限问题。或者只是假设你在 Linux 上 chmod 。您也可以尝试将其放在隐藏/受保护的目录中。

      【讨论】:

      • 由于操作系统在文件系统和用户管理方面的差异很大,因此很难。您可以尝试使用 java File 对象并在其上调用 setReadable / setWritable - 请参阅java.sun.com/javase/6/docs/api/java/io/…
      • 非常聪明的答案。它甚至是跨平台的。它不需要 3rd 方模拟框架。如果你问我,应该是公认的答案。
      【解决方案6】:

      您可以通过在 BufferedReader 上调用 close 方法来强制异常:

          reader = new BufferedReader(new FileReader(csvFile));
      
          // invoke the Close() method.
          reader.Close();
      
          String rawLine;
          while ((rawLine = reader.readLine()) != null) {
              // some work is done here
          }
      

      希望对你有帮助。

      【讨论】:

      • 这很好用
      【解决方案7】:

      您可以从 JDK7 模拟 java.nio.file.FileSystem。我写了http://github.com/dernasherbrezon/mockfs专门用于在测试期间生成IOExceptions。

      【讨论】:

        【解决方案8】:

        您可以使用 MockitoEasymock (+classpath) 之类的 Mock 库来创建 Mock 文件对象(较新的库具有类加载器扩展,可以让您模拟 File 等具体类),或者可以与 PowerMock (see blog) 之类的东西合作并拥有为构造函数调用生成的模拟,并在调用时抛出适当的异常。

        【讨论】:

        • 编辑了答案以链接到有关使用 PowerMock 进行此类测试的博客条目(利用 ClassLoader 操作)
        【解决方案9】:

        您可以使用例如 Mockito 来模拟它,但是您需要重构您的代码,以便更好地测试它。如果您使用 java 7+,我还建议使用 try-with-resources。你的代码看起来会更干净。

        您可以将读取器创建提取到一个单独的方法中,这样您就可以用模拟替换实现。像这样

        import java.io.BufferedReader;
        import java.io.FileNotFoundException;
        import java.io.FileReader;
        import java.io.IOException;
        
        public class SomeClass {
        
            public static final String IO_EXCEPTION = "IO Exception";
            private String csvFile ="some_path";
        
            public void someMethod() {
                BufferedReader reader = null;
                try {
                    reader = getReader();
                    String rawLine;
                    while ((rawLine = reader.readLine()) != null) {
                        // some work is done here
                    }
        
                } catch (FileNotFoundException e) {
                    throw new SomeCustomException("FNF Exception");
                } catch (IOException e) {
                    throw new SomeCustomException(IO_EXCEPTION);
                } finally {
                    // close the input stream
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException e) {
                            // ignore
                        }
                    }
                }
            }
        
            BufferedReader getReader() throws FileNotFoundException {
                return new BufferedReader(new FileReader(csvFile));
            }
        
            class SomeCustomException extends RuntimeException {
        
                public SomeCustomException(String message) {
                    super(message);
                }
            }
        }
        

        测试如下所示

            import static org.assertj.core.api.Assertions.assertThatThrownBy;
        
        import SomeCustomException;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.mockito.Mock;
        import org.mockito.Mockito;
        import org.mockito.runners.MockitoJUnitRunner;
        
        import java.io.BufferedReader;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        
        @RunWith(MockitoJUnitRunner.class)
        public class SomeClassTest {
        
            @Mock
            private BufferedReader bufferedReader;
        
            @Test
            public void testMethod() throws IOException {
                Mockito.when(bufferedReader.readLine()).thenThrow(new IOException());
        
                SomeClass someClass = new SomeClass() {
                    @Override
                    BufferedReader getReader() throws FileNotFoundException {
                        return bufferedReader;
                    }
                };
                assertThatThrownBy(() -> someClass.someMethod()).isInstanceOf(SomeCustomException.class)
                                                                .hasMessage(SomeClass.IO_EXCEPTION);
            }
        }
        

        如果你使用 try-with-resource,这就是你的 someMethod 的样子

        public void someMethod() {
            try(BufferedReader reader = getReader()) {
                String rawLine;
                while ((rawLine = reader.readLine()) != null) {
                    // some work is done here
                }
        
            } catch (FileNotFoundException e) {
                throw new SomeCustomException("FNF Exception");
            } catch (IOException e) {
                throw new SomeCustomException(IO_EXCEPTION);
            }
        }
        

        短 2 倍,可读性提高 20 倍

        PS 作为测试的另一个选项,您可以在测试类中扩展您的 SomeClass 并在测试类中覆盖 someMethod 而不是创建一致的实现。但我更喜欢第一个选项。不过,这是一个品味问题。

        希望对您有所帮助。

        PPS:刚刚意识到这个问题是几年前提出的。 :) 希望这几天它可以帮助某人找到答案。

        【讨论】:

          【解决方案10】:

          您可以针对太大的文件引发此异常。

          【讨论】:

            【解决方案11】:

            我建议不要使用 FileNotFoundException。如果您想为不存在的文件抛出特定异常,我会检查 csvFile.exists()。

            例如,您可以将 csvFile 设为目录。我测试了你的代码,它抛出了:

            File file = new File("actually_a_directory.csv"); file.mkdir(); yourCode(file); // throws FileNotFoundException: "actually_a_directory.csv (Access is denied)"

            在我看来,实际上是目录的文件与 FileNotFound 不同,但它在 java 中的作用相同。

            【讨论】:

              【解决方案12】:

              你总是可以抛出自己的IOException

              throw new IOException("Test IOException");
              

              【讨论】:

              • 我为原始问题添加了代码。看来你没看懂。
              猜你喜欢
              • 2021-03-04
              • 1970-01-01
              • 2020-02-01
              • 1970-01-01
              • 1970-01-01
              • 2015-06-23
              • 2022-12-11
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多