模拟Java输入流

95

请给我一些指针来帮助我模拟那个Java InputStream对象。这是我想要模拟的代码行:

InputStreamReader inputData = new InputStreamReader(System.in);
bufferdReader = new BufferedReader(inputData);
bufferdReader.readLine(); 

1
你的问题是 System.in 吗?你可能想重构你的代码,以便传递另一个 InputStream 而不是 System.in;然后你可以使用任何 Mocking 框架(如答案中提到的)来模拟这个 InputStream。 - phtrivier
11个回答

131
你可以使用commons-io创建一些存根输入流:
InputStream stubInputStream = 
     IOUtils.toInputStream("some test data for my input stream", "UTF-8");

1
如果您想要多次使用该流,您如何在使用后重置它? - exic
您可以将其包装在BufferedInputStream中,并使用mark()和reset()。但是在设置方法中实例化一个新的可能会更容易(假设我们仍在谈论测试)。 - Eric
1
自从v2.3版本以后,您需要添加字符集,例如:toInputStream(“someString”,“UTF-8”)。仅带有字符串参数的版本已不建议使用。 - JonyD
谢谢@JonyD!我更新了答案,包括字符集参数。 - Eric
1
我最喜欢的答案!我只是想补充一下,您可以使用StandardCharsets.UTF_8代替非类型安全的字符串。 - dcasadevall

129
你可以直接使用一个 ByteArrayInputStream,并用你的测试数据填充它。

@Brad在评论中提供了一个示例:

InputStream anyInputStream = new ByteArrayInputStream("test data".getBytes());

ιΔùεΛ•γö³εΞΫεΛ³φ‰·ByteArrayInputStreamφî·φ¨¹mark()壨reset()ψIJ - Hank
5
简单明了... InputStream anyInputStream = new ByteArrayInputStream("test data".getBytes()); (将字符串"test data"转换成字节数组,并创建一个ByteArrayInputStream对象anyInputStream来读取这个字节数组中的数据) - Brad
2
在 Kotlin 中:val inputStream = "test data".byteInputStream() - swooby

66
BufferedReader bufferedReader = org.mockito.Mockito.mock(BufferedReader.class);
when(bufferedReader.readLine())
  .thenReturn("first line")
  .thenReturn("second line");

org.junit.Assert.when(new Client(bufferedReader).parseLine())
  .thenEquals(IsEqual.equalTo("first line"));

我该如何导入 Client?很抱歉,我对mockito和junit不太熟悉,您在这一行中使用了Client: org.junit.Assert.when(new Client(bufferedReader).parseLine()) - Alejandro Sanchez
客户端是一个类,它使用缓冲读取器逐行读取并解析数据。 - Boris Pavlović

21

我不同意这个问题选定的答案。像Mockito这样的模拟框架虽然不错,但是当标准Java API可用时,考虑使用它们可能更好。

BufferedReader reader = new BufferedReader(new StringReader("some string"));

当你可以使用一个具有所有状态和行为的真实对象时,为什么要在测试类中使用 Mock 对象呢?

要了解更多关于此工作原理的信息,可以查找“装饰器”设计模式。


5
以下是一个使用案例:模拟多个不同的调用,例如在循环中(错误输入1 -> 错误输入2 -> 正确输入3)。 - Jan Groth
将InputStream代码和Reader代码保持在不同的类中,就像Java API一样。测试类应该传递一个Reader实现,而不是InputStream,在生产代码中也应该向读取数据/处理数据的类传递一个Reader实现。 - John Deverall

4
@Test
    public void testReadFile() {
    TestClass ClassName = Mockito.mock(TestClass.class);
     InputStream in = Mockito.mock(InputStream.class);
     InputStreamReader inr =Mockito.mock(InputStreamReader.class);
     BufferedReader bufferedReader =Mockito.mock(BufferedReader.class);
       try {
         PowerMockito.whenNew(InputStreamReader.class).withArguments(in).thenReturn(inr);
         PowerMockito.whenNew(BufferedReader.class).withArguments(inr).thenReturn(bufferedReader);
         String line1 = "example line";
         PowerMockito.when(bufferedReader.readLine()).thenReturn(line1).thenReturn(null);
         method return type = Whitebox.invokeMethod(ClassName, "MethodName", arguement);
         assertEquals("result is::","expected", actual);
     } catch (Exception e) {
         e.printStackTrace();
     }
 }

3
将您的对象更改为更易于测试,就像这样:
public MyObject {
    private InputStream inputStream;

    public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}

    public void whatever() {
        InputStreamReader inputData = new InputStreamReader(inputStream);
        bufferdReader = new BufferedReader(inputData);
        bufferdReader.readLine(); 
    }
}

然后当您使用对象时,请先初始化其inputStream:

MyObject myObject = new MyObject();
myObject.setInputStream(System.in);

现在您有一个对象,可以使用任何InputStream的实现来测试它(ByteArrayInputStream是一个不错的选择)。

3
String testString = "test\nstring";
InputStream stream = new ByteArrayInputStream(testString.getBytes(StandardCharsets.UTF_8));

BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

Assert.assertEquals("test", reader.readLine());
Assert.assertEquals("string", reader.readLine());

1
我找到的最好的解决方案是使用


final InputStream inputStream1 = IOUtils.toInputStream("yourdata");

然后将输入流包装在BufferedReader中,这是编写围绕输入流进行测试的最佳方式。


1
这会增加对IOUtils的依赖 - 这会使您的测试代码变得更大。这不是我们从小型单元测试中想要的效果... - IgorGanapolsky

0
假设您正在使用Maven,您可以将资源放入“src/test/resources/”文件夹中,比如说“src/test/resources/wonderful-mock-data.xml”。然后在JUnit中,您可以这样做:
    String resourceInputFile = "/database-insert-test.xml";
    URL url = this.getClass().getResource(resourceInputFile);
    Assert.assertNotNull("Can't find resource " + resourceInputFile, url);

    InputStream inputStream = url.openStream();

    // Now you can just use the inputStream for method calls requiring this param
    (...)

在这个例子中,如果给定的资源在当前类路径中找不到,url变量将为null。这种方法允许您将多个场景放置在不同的resourceInputFile(s)中...还要记住,“src/test/resources/”下的所有资源(不仅仅是xml文件,任何类型的资源如txt、html、jpeg等)通常都可以作为类路径资源从所有jUnit测试中访问。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接