如何模拟FileInputStream和其他*流



我有一个类,它获取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;
}
}

问题在于新的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;
}
}

所以现在我可以覆盖方法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方法现在包含了测试,但我仍然发现了导致cobertura问题的方法getBufferedReader。如何测试方法getBufferedReader,或者可能有其他问题的解决方案?

您可以使用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 
}
}

也许这是个坏主意,但我的第一种方法是创建一个实际的测试文件,而不是模拟流对象。

有人可能会说,这将测试GenericFile类,而不是getBufferedReader方法。

也许一种可以接受的方法是通过模拟的GenericFile返回一个实际存在的测试文件来测试getBufferedReader

我首先将Stream的创建提取到一个依赖项中。因此,您的RealCardParser会获得一个StreamSource作为依赖项。

现在你可以看到你的问题:

  1. 为您当前的测试提供一个mock(或者在这种情况下,我更喜欢一个假的)实现,返回由String构建的Stream。

  2. 用一个真实的文件测试实际的StreamSource,确保它返回正确的内容。

我知道这不是你想要的答案。

单元测试的理念是确保您的逻辑是正确的。单元测试会发现编写了错误逻辑的错误。如果一个方法不包含逻辑(也就是说,没有分支、循环或异常处理),那么对它进行单元测试是不经济的。我的意思是,单元测试需要花钱——写它的时间和维护它的时间。大多数单元测试都会回报我们的投资,要么是发现错误,要么是向我们保证在测试的领域中没有错误。

但是,对您的getBufferedReader方法进行单元测试不会回报您的投资。它的成本有限,但收益为零,因为没有可能出错的实际逻辑。因此,您不应该编写这样的单元测试。如果您的Cobertura设置或组织标准要求存在这样的单元测试,那么这些设置或标准是错误的,应该进行更改。否则,你雇主的钱就会花在成本效益比无限大的事情上。

我强烈建议您更改标准,以便只为包含分支、循环或异常处理的方法编写单元测试。

当您有这个问题时。您可能没有正确地遵循依赖性反转原则。您应该尽可能使用InputStream。如果您这样编写FileInputStream适配器方法:

class FileReader {
public InputStream readAsStream() {
return new FileInputStream("path/to/File.txt");
}
}

然后,您可以模拟该方法以返回ByteArrayInputStream。这更容易处理,因为您只需要向流传递一个字符串,而不需要处理特定的FileInputStream实现。

如果您使用mockito进行mock,示例如下:

FileReader fd = mock(FileReader());
String fileContent = ...;
ByteArrayInputStream bais = new ByteArrayInputStream(fileContent);
when(fd.readAsStream()).thenReturn(bais);

相关内容

  • 没有找到相关文章

最新更新