4

我有一个获取 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 或者问题的另一种解决方案?

4

5 回答 5

3

You can mock FileInputStream by using PowerMockRunner and PowerMockito. See the below code for mocking-

@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 

        }
}
于 2013-07-30T10:48:12.337 回答
2

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.

Maybe an acceptable way would be to return an actually existing test-file through the mocked GenericFile for testing the getBufferedReader?

于 2012-09-26T06:54:08.477 回答
2

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

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

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

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

于 2012-09-26T19:31:10.540 回答
2

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

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

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

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

于 2012-09-26T07:06:35.977 回答
1

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

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

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

如果你使用 mockito 来模拟,示例如下:

FileReader fd = mock(FileReader());
String fileContent = ...;
ByteArrayInputStream bais = new ByteArrayInputStream(fileContent);
when(fd.readAsStream()).thenReturn(bais);
于 2019-10-10T02:33:43.607 回答