在我的属性文件中放入什么值才能在加载时获得 IOException?

What values to put in my properties file to get an IOException while loading?

我正在从 Java 项目的类路径中读取一个文件。

示例代码:

      public static Properties loadPropertyFile(String fileName) {
        Properties properties = new Properties();    
        InputStream inputStream = PropertyReader.class.getClassLoader().getResourceAsStream(fileName);

        if (inputStream != null) {
            try {
                properties.load(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            throw new Exception("Property file: [" + fileName + "] not found in the classpath");
        }
        return properties;
      }

一切正常。我正在为此代码编写 Junit 测试。如何在 properties.load(inputStream) 中为 IOException 创建场景?

我应该在 properties.file 中输入什么值才能得到 IOException

空的或无效的 fileName 参数。

传递一个空字符串作为文件名

当您查看 Properties::load 的实现时,您会发现 class 从不显式抛出异常。触发 IOException 的唯一方法是提交一个 InputStream,它会在调用输入流的 read 方法时抛出此异常。

您可以控制 PropertyReader class 吗?模拟此错误的一种方法是 instrument this class loader 到 return 给定测试值 fileName 的错误 InputStream 以抛出 IOException。或者,您可以通过将签名更改为:

来使该方法更加灵活
public static Properties loadPropertyFile(String fileName) {
  return loadPropertyFile(fileName, PropertyReader.class);
}

public static Properties loadPropertyFile(String fileName, ClassLoader cl) {
  // your code...
}

用 class 加载程序:

class TestLoader extends ClassLoader {
  @Override
  public InputStream getResourceAsStream() {
    return new InputStream() {
      @Override
      public byte read() throws IOException {
        throws new IOException();
      }
    }
  }
}

您不能将导致 IOException 的特定字符添加到属性文件中,因为 InputStream 仅读取字节。任何与编码相关的问题都会导致 IllegalArgumentException.

你考虑过模拟框架吗?将属性的 new 运算符模拟为 return 一个模拟实现,当其 load 方法被调用时抛出 IOException

mockitio 和 powermockito 将允许您执行此操作。

强制 load 调用抛出 IOException 的一种方法是传递一个关闭的流。如果您可以重构您的方法以接受 InputStream,您可以将一个关闭的流传递给该方法并检查它是否抛出异常。

也就是说,单元测试应该涵盖您编写的代码。在我看来,如果输入流有错误,您正在测试 load 是否抛出异常。这是多余的。

我知道这是一个旧线程,但自从我最近 运行 遇到同样的问题并基于 我想出了这个测试:

@Test
void testFailLoadProjectFile() throws NoSuchMethodException, SecurityException {
    final var method = LogGeneratorComponent.class.getDeclaredMethod("loadProjectProperties", Properties.class);

    Assertions.assertTrue(Modifier.isPrivate(method.getModifiers()), "Method is not private.");

    method.setAccessible(true);

    final var obj = new LogGeneratorComponent();

    Assertions.assertThrows(InvocationTargetException.class, () -> method.invoke(obj, new Properties() {

        private static final long serialVersionUID = 5663506788956932491L;

        @Override
        public synchronized void load(@SuppressWarnings("unused") final InputStream is) throws IOException {
            throw new IOException("Invalid properties implementation.");
        }
    }), "An InvocationTargetException should have been thrown, but nothing happened.");
}

这是我的实际方法:

private static Properties loadProjectProperties(final Properties properties) {
    try (final var is = Thread.currentThread().getContextClassLoader().getResourceAsStream("project.properties")) {
        properties.load(is);
        return properties;
    } catch (final IOException e) {
        throw new UncheckedIOException("Error while reading project.properties file.", e);
    }
}

显然 OP 可以自定义它以接收 fileName 参数并使其成为 public 以便进行更简单的测试。