java 如何模拟 FileInputStream 和其他 *Streams
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12596075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to mock FileInputStream and other *Streams
提问by Dmitrii Borovoi
I have class that gets GenericFile as input argument reads data and does some additional processing. I need to test it:
我有一个类将 GenericFile 作为输入参数读取数据并进行一些额外的处理。我需要测试一下:
public class RealCardParser {
public static final Logger l = LoggerFactory.getLogger(RealCardParser.class);
@Handler
public ArrayList<String> handle(GenericFile genericFile) throws IOException {
ArrayList<String> strings = new ArrayList<String>();
FileInputStream fstream = new FileInputStream((File) genericFile.getFile());
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine = br.readLine();//skip header
while ((strLine = br.readLine()) != null) {
l.info("handling in parser: {}", strLine);
strings.add(strLine);
}
br.close();
return strings;
}
}
The issue is with new FileInputStream. I can mock GenericFile but it is useless cause FileInputStream checks if file exists. I changed my class so:
问题在于新的 FileInputStream。我可以模拟 GenericFile 但它没有用,因为 FileInputStream 检查文件是否存在。我改变了我的课程:
public class RealCardParser {
public static final Logger l = LoggerFactory.getLogger(RealCardParser.class);
protected BufferedReader getBufferedReader(GenericFile genericFile) throws FileNotFoundException {
FileInputStream fstream = new FileInputStream((File) genericFile.getFile());
DataInputStream in = new DataInputStream(fstream);
return new BufferedReader(new InputStreamReader(in));
}
@Handler
public ArrayList<String> handle(GenericFile genericFile) throws IOException {
ArrayList<String> strings = new ArrayList<String>();
BufferedReader br = getBufferedReader(genericFile);
String strLine = br.readLine();//skip header
while ((strLine = br.readLine()) != null) {
l.info("handling in parser: {}", strLine);
strings.add(strLine);
}
br.close();
return strings;
}
}
So now I can override method getBufferedReader and test method handler:
所以现在我可以覆盖方法 getBufferedReader 和测试方法处理程序:
@RunWith(MockitoJUnitRunner.class)
public class RealCardParserTest {
RealCardParser parser;
@Mock
GenericFile genericFile;
@Mock
BufferedReader bufferedReader;
@Mock
File file;
@Before
public void setUp() throws Exception {
parser = new RealCardParser() {
@Override
public BufferedReader getBufferedReader(GenericFile genericFile) throws FileNotFoundException {
return bufferedReader;
}
};
when(genericFile.getFile()).thenReturn(file);
when(bufferedReader.readLine()).thenReturn("header").thenReturn("1,2,3").thenReturn(null);
}
@Test
public void testParser() throws Exception {
parser.handle(genericFile);
//do some asserts
}
}
Handler method now is covered with tests, but I still have uncovered method getBufferedReader that leads to cobertura problems. How to test method getBufferedReader or maybe there is another solution of the problem?
处理程序方法现在已包含测试,但我仍然发现了导致 cobertura 问题的方法 getBufferedReader。如何测试方法 getBufferedReader 或者可能有另一个问题的解决方案?
回答by Fildor
Maybe this is a bad idea, but my first approach would have been creating an actual test-file rather than mocking the stream object.
也许这是一个坏主意,但我的第一种方法是创建一个实际的测试文件,而不是模拟流对象。
One could argue that this would test the GenericFile
class rather than the getBufferedReader
method.
有人可能会争辩说,这将测试GenericFile
类而不是getBufferedReader
方法。
Maybe an acceptable way would be to return an actually existing test-file through the mocked GenericFile
for testing the getBufferedReader
?
也许一种可以接受的方法是通过模拟返回一个实际存在的测试文件来GenericFile
测试getBufferedReader
?
回答by Jens Schauder
I would first extract the creation of the Stream into a dependency. So your RealCardParser gets a StreamSource as a dependency.
我首先将 Stream 的创建提取到依赖项中。因此,您的 RealCardParser 将 StreamSource 作为依赖项。
Now you can take appart your problem:
现在你可以解决你的问题:
for your current test provide a mock (or in this case I would prefer a fake) implementation returning a Stream constructed from a String.
Test the actual StreamSource with a real file, ensuring that it returns the correct content and what not.
为您当前的测试提供一个模拟(或者在这种情况下我更喜欢一个假的)实现返回一个从字符串构造的流。
用一个真实的文件测试实际的 StreamSource,确保它返回正确的内容,什么不是。
回答by Bhabatosh
You can mock FileInputStream by using PowerMockRunner and PowerMockito. See the below code for mocking-
您可以使用 PowerMockRunner 和 PowerMockito 模拟 FileInputStream。请参阅以下代码进行模拟-
@RunWith(PowerMockRunner.class)
@PrepareForTest({
FileInputStream.class
})
public class A{
@Test
public void testFileInputStream ()
throws Exception
{
final FileInputStream fileInputStreamMock = PowerMockito.mock(FileInputStream.class);
PowerMockito.whenNew(FileInputStream.class).withArguments(Matchers.anyString())
.thenReturn(fileInputStreamMock);
//Call the actual method containing the new constructor of FileInputStream
}
}
回答by Peter Paul
When you are having this question. You are probably not following dependency inversion principlecorrectly. You should use InputStream whenever it's possible. If your write your FileInputStream adapter method like this:
当你有这个问题的时候。您可能没有正确遵循依赖倒置原则。您应该尽可能使用 InputStream。如果您像这样编写 FileInputStream 适配器方法:
class FileReader {
public InputStream readAsStream() {
return new FileInputStream("path/to/File.txt");
}
}
Then you can mock the method to return ByteArrayInputStream alternatively. This is much easier to deal with, because you only need to pass a string to the stream instead of dealing with the specific FileInputStream implementation.
然后您可以模拟该方法以交替返回 ByteArrayInputStream。这更容易处理,因为您只需要向流传递一个字符串,而不需要处理特定的 FileInputStream 实现。
If you are using mockito to mock, the sample goes like this:
如果您使用 mockito 来模拟,示例如下:
FileReader fd = mock(FileReader());
String fileContent = ...;
ByteArrayInputStream bais = new ByteArrayInputStream(fileContent);
when(fd.readAsStream()).thenReturn(bais);
回答by Dawood ibn Kareem
I know this isn't the answer that you want.
我知道这不是你想要的答案。
The idea of unit testing is to make sure your logic is correct. Unit tests catch bugs where incorrect logic has been written. If a method contains no logic (that is, no branching, looping or exception handling), then it is uneconomical to unit test it. By that, I mean that a unit test costs money - time to write it, and time to maintain it. Most unit tests pay us back for that investment, either by finding bugs, or re-assuring us that there are no bugs in the domain of what is being tested.
单元测试的想法是确保您的逻辑是正确的。单元测试可捕获编写了不正确逻辑的错误。如果一个方法不包含逻辑(即没有分支、循环或异常处理),那么对其进行单元测试是不经济的。我的意思是,单元测试要花钱——编写它的时间和维护它的时间。大多数单元测试通过查找错误或重新向我们保证所测试的领域中没有错误来回报我们的投资。
But a unit test for your getBufferedReader
method would not pay you back for our investment. It has a finite cost, but zero benefit, because there is no actual logic that can go wrong. Therefore, you should NOT write such a unit test. If your Cobertura settings or your organisational standards require the existence of such a unit test, then those settings or standards are WRONG and should be changed. Otherwise, your employer's money is being spent on something that has an infinite cost:benefit ratio.
但是对您的getBufferedReader
方法进行单元测试不会为您的投资带来回报。它的成本有限,但收益为零,因为没有可能出错的实际逻辑。因此,您不应该编写这样的单元测试。如果您的 Cobertura 设置或您的组织标准要求存在这样的单元测试,那么这些设置或标准是错误的,应该更改。否则,您雇主的钱就会花在具有无限成本:收益比的东西上。
I strongly recommend that your standards are changed so that you only write unit test for methods that contain branching, looping or exception handling.
我强烈建议您更改标准,以便您只为包含分支、循环或异常处理的方法编写单元测试。