如何模拟FileInputStream和其他* Streams

问题描述 投票:1回答:5

我有一个将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或也许还有另一种解决方法?

java junit inputstream mockito
5个回答
2
投票

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

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

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


2
投票

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

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

  1. 为您当前的测试提供一个模拟(或者在这种情况下,我希望使用伪造的)实现,该实现返回由字符串构造的Stream。

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

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

        }
}

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

单元测试的想法是确保您的逻辑正确。单元测试会捕获错误的逻辑编写错误。如果一个方法不包含逻辑(即,没有分支,循环或异常处理),则对它进行单元测试是不经济的。我的意思是,单元测试要花钱-写时间和维护时间。大多数单元测试都会通过发现错误或使我们确信在所测试内容中没有错误来偿还这笔投资。

但是对您的getBufferedReader方法进行单元测试不会使您收回我们的投资。它具有有限的成本,但收益为零,因为没有实际的逻辑可能出错。因此,您不应编写此类单元测试。如果您的Cobertura设置或组织标准要求存在此类单元测试,则这些设置或标准是错误的,应进行更改。否则,您的老板的钱就花在了成本与收益之比无穷大的东西上。

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

[您有此问题时。您可能未正确遵循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);

2
投票

您可以使用PowerMockRunner和PowerMockito模拟FileInputStream。参见下面的模拟代码-


1
投票

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.