如何模拟FileInputStream和其他* Streams

时间:2012-09-26 06:50:04

标签: java junit inputstream mockito

我有一个获取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
    }
}

处理程序方法现在已经涵盖了测试,但我仍然发现了导致cobertura问题的方法getBufferedReader。 如何测试方法getBufferedReader或者是否有另一个问题的解决方案?

5 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

我首先将Stream的创建提取为依赖项。因此,您的RealCardParser将StreamSource作为依赖项。

现在你可以解决你的问题:

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

  2. 使用真实文件测试实际的StreamSource,确保它返回正确的内容,而不是。

答案 2 :(得分:2)

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

        }
}

答案 3 :(得分:1)

当您有此问题时。您可能未正确遵循dependency inversion principle。您应该尽可能使用InputStream。如果您这样编写FileInputStream适配器方法:

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

然后,您可以模拟该方法以交替返回ByteArrayInputStream。这很容易处理,因为您只需要将字符串传递给流,而不用处理特定的FileInputStream实现。

如果您要使用模仿进行模拟,示例将如下所示:

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

答案 4 :(得分:0)

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

单元测试的想法是确保您的逻辑是正确的。单元测试可以捕获写入错误逻辑的错误。如果方法不包含逻辑(即没有分支,循环或异常处理),那么对它进行单元测试是不经济的。通过这种方式,我的意思是单元测试花费金钱 - 写它的时间,以及维护它的时间。大多数单元测试都会通过发现错误或者向我们重新确认在测试内容中没有错误来支付我们的投资回报。

但是,getBufferedReader方法的单元测试不会为您的投资支付费用。它具有有限的成本,但没有任何好处,因为没有可能出错的实际逻辑。因此,您不应该编写这样的单元测试。如果您的Cobertura设置或您的组织标准要求存在此类单元测试,那么这些设置或标准是错误的,应该更改。否则,您的雇主的钱将花在具有无限成本:效益比的事物上。

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