编写单元测试是软件开发的组成部分。 当您的被测类与操作系统交互时,您必须解决的一个问题是模拟其行为。 这可以通过使用模拟代替Java Runtime Environment(JRE)提供的实际对象来完成。 支持Java的模拟的库是例如嘲笑或jMock 。
当您完全控制对象的实例化时,模拟对象是一件好事。 在处理标准输入和标准输出时,这有点棘手,但并非不可能,因为java.lang.System
允许您替换标准InputStream
和OutputStream
。
System.setIn(in);
System.setOut(out);
为了不必在每个测试用例前后都手动替换流,可以使用org.junit.rules.ExternalResource
。 此类提供了两个方法before()
和after()
,就像它们的名称所暗示的那样,在每个测试用例之前和之后调用。 这样,您可以轻松地设置和清除一类中所有测试所需的资源。 或者,回到原始问题,替换java.lang.System
的输入和输出流。
我上面所描述的完全由库system-rules实现 。 要查看其工作原理,让我们从一个简单的示例开始:
public class CliExample {private Scanner scanner = new Scanner(System.in, "UTF-8");public static void main(String[] args) {CliExample cliExample = new CliExample();cliExample.run();}private void run() {try {int a = readInNumber();int b = readInNumber();int sum = a + b;System.out.println(sum);} catch (InputMismatchException e) {System.err.println("The input is not a valid integer.");} catch (IOException e) {System.err.println("An input/output error occurred: " + e.getMessage());}}private int readInNumber() throws IOException {System.out.println("Please enter a number:");String nextInput = scanner.next();try {return Integer.valueOf(nextInput);} catch(Exception e) {throw new InputMismatchException();}}
}
上面的代码从标准输入中读取两个整数,并打印出其总和。 如果用户提供了无效的输入,则程序应在错误流上输出适当的消息。
在第一个测试用例中,我们要验证程序是否正确地将两个数字求和并打印出结果:
public class CliExampleTest {@Rulepublic final StandardErrorStreamLog stdErrLog = new StandardErrorStreamLog();@Rulepublic final StandardOutputStreamLog stdOutLog = new StandardOutputStreamLog();@Rulepublic final TextFromStandardInputStream systemInMock = emptyStandardInputStream();@Testpublic void testSuccessfulExecution() {systemInMock.provideText("2\n3\n");CliExample.main(new String[]{});assertThat(stdOutLog.getLog(), is("Please enter a number:\r\nPlease enter a number:\r\n5\r\n"));}...
}
为了模拟System.in
我们利用系统规则的TextFromStandardInputStream
。 通过调用emptyStandardInputStream()
用一个空的输入流初始化实例变量。 在测试用例本身中,我们通过在适当的位置调用带有换行符的provideText()
来为应用程序提供信息。 然后,我们调用应用程序的main()
方法。 最后,我们必须断言该应用程序已将两个输入语句和结果写入标准输入。 后者是通过StandardOutputStreamLog
实例完成的。 通过调用其方法getLog()
我们可以检索在当前测试用例中已写入标准输出的所有内容。
StandardErrorStreamLog
可以类似地用于验证写入标准错误的内容:
@Test
public void testInvalidInput() throws IOException {systemInMock.provideText("a\n");CliExample.main(new String[]{});assertThat(stdErrLog.getLog(), is("The input is not a valid integer.\r\n"));
}
除此之外, system-rules
还提供了使用System.getProperty()
, System.setProperty()
, System.exit()
和System.getSecurityManager()
。
结论 :通过系统规则测试,带有单元测试的命令行应用程序比使用junit的规则本身更加简单。 每个测试用例前后的所有样板代码都在一些易于使用的规则之内,用于更新系统环境。
PS:您可以在此处找到完整的资源。
翻译自: https://www.javacodegeeks.com/2015/01/testing-system-in-and-system-out-with-system-rules.html