最近,我的一些学生向我询问了赫尔辛基大学MOOC提供的单元测试的机制,我检查了它们的实现,并认为这对于初学者了解实际发生的情况是有帮助的,因此在此发表了这篇小文章。

我们将以“机场”项目为例,这是OOP2第一周的最后一项任务。

我们仅关注测试,因此我将跳过有关如何解决它的事情。 在本练习中,我们将每次手动执行main方法,重复输入飞机编号,容量,有时我们认为我们的代码可以工作,然后运行本地测试,以便可以提交给服务器进行在线判断和评分。


我一直使用这个小项目作为借助单元测试保护的重构示例。 当我又重复又痛苦地输入飞机ID,航班号,机场代码和操作代码时,我问我的学生:“这很痛苦吗?”。

显然,他们所有人都回答了。 然后我问,“即使无聊又痛苦,您会一次又一次地进行这种测试吗?”

安静。

从我过去的经验中,我知道跳过这些无聊的测试很容易,并且我们可以安慰自己,“这些代码非常简单,我不会犯错,它将起作用并且会起作用,不用担心。”

由于做出这样的选择,我会留下痛苦的回忆,因为过去我犯了太多简单而愚蠢的错误,所以无论看起来多么简单,我仍然会进行测试-即使是手动测试,也无聊而痛苦。

我添加此内容是因为单元测试无法完全替代手动测试,尽管它将使手动测试更加容易和有效。

对于Airport项目,如果不需要每次都重复输入,并且可以捕获程序的输出,则与预期相比,我们将更快地获得反馈。

String operation = scanner.nextLine();
...
System.out.println("Blahblahblah...");

例如,我们确切地知道是否首先输入x ,然后它将进入飞行服务部分并打印菜单选项;如果我们第二次输入x ,则程序将结束循环并退出,结果,我们将仅获取机场面板和飞行服务的说明输出。

因此,让我们转到一个测试用例,看看实际会发生什么。

@Test
public void printsMenusAndExits() throws Throwable {String syote = "x\nx\n";MockInOut io = new MockInOut(syote);suorita(f(syote));String[] menuRivit = {"Airport panel","[1] Add airplane","[2] Add flight","[x] Exit","Flight service","[1] Print planes","[2] Print flights","[3] Print plane info","[x] Quit"};String output = io.getOutput();String op = output;for (String menuRivi : menuRivit) {int ind = op.indexOf(menuRivi);assertRight(menuRivi, syote, output, ind > -1);op = op.substring(ind + 1);}
}

上面是第二个测试用例,它涵盖了我们所说的最简单的情况,仅输入两个x

当我们查看测试代码时,它分为三部分:

  • 准备输入
  • 执行Main.main(args)方法
  • 检查输出以查看它是否依次包含所有预期行

您知道scanner.nextLine()scanner.nextInt()的正常行为。 该程序将挂起并等待用户输入,以便执行下一行代码。 但是,为什么它在这里没有任何等待就可以顺利运行?

在开始这一部分之前,我想简要解释一下该方法的执行,它使用Java反射以一种不直接但可以进行更多检查的方式来调用该方法,例如,第一个测试用例要求Main为公共类,但您可能会发现要通过手动测试,可以将Main访问级别设置为package。

@Test
public void classIsPublic() {assertTrue("Class " + klassName + " should be public, so it must be defined as\n" +"public class " + klassName + " {...\n}", klass.isPublic());
}

这里klass.isPublic()正在检查是否根据需要设置访问级别。

好。 看来MockInOut类使魔术发生了,我们可以检查代码以在MockInOut找到想法。 您可以在GitHub上访问源代码。

public MockInOut(String input) {orig = System.out;irig = System.in;os = new ByteArrayOutputStream();try {System.setOut(new PrintStream(os, false, charset.name()));} catch (UnsupportedEncodingException ex) {throw new RuntimeException(ex);}is = new ByteArrayInputStream(input.getBytes());System.setIn(is);
}

您可能已经输入System.out数千次,但是您是否意识到可以像上面一样默默地更改out ? 这同时设置outin系统的,这样我们就可以完全执行后得到的输出,我们也不需要手工输入这个时候,因为在声明Scanner scanner = new Scanner(System.in); ,则参数System.in会以无提示方式更改,因此scanner.nextLine()将获得准备好的输入而不会挂起。

同样,输出将不会在控制台中打印,而是会累积到ByteArrayOutputStream ,此后可以访问。

您可能想知道,如果我们真的要恢复System.inSystem.out的正常行为,该怎么办?

/*** Restores System.in and System.out*/
public void close() {os = null;is = null;System.setOut(orig);System.setIn(irig);
}

基本上,它节省了原来inout ,需要恢复时,只需再次清除遭入侵的人,并设置他们回来,那么一切都将照常进行。

您可以在下面复制简单的示例代码以进行快速测试。

import java.io.*;
import java.util.*;class HelloWorld {public static void main(String[] args) throws IOException {PrintStream orig = System.out;ByteArrayOutputStream os = new ByteArrayOutputStream();System.setOut(new PrintStream(os, false, "UTF-8"));// Here it won't print but just accumulatefor (int i = 0; i < 100; i++) {System.out.println("Hello World");}System.setOut(orig);// Print 100 lines of "Hello World" here since out was restoredSystem.out.println(os.toString("UTF-8"));InputStream is = System.in;System.setIn(new ByteArrayInputStream("x\nx\n".getBytes()));Scanner scanner = new Scanner(System.in);// Without hang onSystem.out.println(scanner.nextLine());System.out.println(scanner.nextLine());try {// There are only two lines provided, so here will failSystem.out.println(scanner.nextLine());} catch (NoSuchElementException e) {e.printStackTrace();}System.setIn(is);scanner = new Scanner(System.in);// Hang on here since `in` was restoredSystem.out.println(scanner.nextLine());}
}

实际上,注入和替换是一种用于分离单元测试依赖关系的常用方法,这对于仅关注代码非常有用。 还有更先进,更复杂的方法来做到这一点,但在这里,我们只是想说明一个简单的方法是“黑客” inout ,这样你可以专注于你的代码,而不是inout

对于某些遗留项目,此方法可能对重构至关重要,因为太多的依赖关系使测试变得非常困难!

翻译自: https://www.javacodegeeks.com/2019/02/approach-simulate-input-check-output.html

模拟用户输入并检查输出的简单方法相关推荐

  1. s71200模拟量输入输出_模拟用户输入并检查输出的简单方法

    s71200模拟量输入输出 最近,我的一些学生向我询问了赫尔辛基大学MOOC提供的单元测试的机制,我检查了它们的实现,并认为这对于初学者了解实际发生的情况是有帮助的,因此在此发表了这篇小文章. 我们将 ...

  2. android跨进程事件注入(程序模拟用户输入)

    转载请注明出处 早想写这篇,一直没空,现在总结下. 需求: 需要在程序内模拟用户输入,比如点击屏幕,或者输入键盘.模拟用户的滑动等.具体的需求,比如测试的时候,测试打开浏览器1000次.或者通过网络发 ...

  3. 用户输入年份,输出当前年份2月份的天数

    用户输入年份,输出当前年份2月份的天数 <!DOCTYPE html> <html lang="en"><head><meta chars ...

  4. 编写一个将用户输入的信息输出到网页的js程序 和 用if else验证用户输入的密码是否正确(两个代码)

    js的一次作业罢了 tip:编写一个将用户输入的信息输出到网页的js程序 和 用if else验证用户输入的密码是否正确 (两个代码) 1.编写一个将用户输入的信息输出到网页的js程序 <!DO ...

  5. Java黑皮书课后题第7章:*7.30(模式识别:四个连续相等的数)编写下面的方法,测试某数组是否有四个连续相同值的数。编写测试程序,提示用户输入一个整数列表,调用方法看是否有4个连续且相等的数

    *7.30(模式识别:四个连续相等的数)编写下面的方法,测试某数组是否有四个连续相同值的数.编写测试程序,提示用户输入一个整数列表,调用方法看是否有4个连续且相等的数 题目 题目描述与运行示例 破题 ...

  6. android模拟用户输入

    目录(?)[-] geteventsendevent input keyevent 本文讲的是通过使用代码,可以控制手机的屏幕和物理按键,也就是说不只是在某一个APP里去操作,而是整个手机系统. ge ...

  7. Android输入模拟——如何模拟用户输入

    1 前言 方案1:参考雄哥明日方舟 使用ADB的CMD命令模拟输入: 2 模拟输入 使用 adb shell:

  8. 油猴脚本——掘金Markdown格式适配器知识点记录【油猴脚本、Markdown、浏览器文件读取、tooltip、SVG、、模拟用户输入、aria-xxxx属性、剪切板操作、】

    油猴脚本--掘金Markdown格式适配器知识点记录 脚本更新日志 参考:掘金Markdown格式适配器更新日志 - 掘金 脚本地址: 更新:2021年9月3日19:57:35 参考:掘金Markdo ...

  9. 根据用户输入编码,输出课程名称

    实现代码: import java.util.InputMismatchException; import java.util.Scanner;public class ExceptionDome1 ...

最新文章

  1. python tk messagebox_tkMessageBox
  2. 在Android 华为手机上运行React_Native工程时出现了如下错误,做以记录:
  3. [转]撞车之后,不要傻里傻气的!
  4. Django 执行 makemigrations 显示 No changes detected in app
  5. 积分上下限无穷_数学方法的思考和存在无穷多个孪生素数证明的思路
  6. SAP OData编程指南
  7. Java EE安全性API向前发展
  8. 微服务 注册中心的作用_102,谈谈微服务注册中心zookeeperamp;Eureka
  9. RTSP播放器或RTMP播放器常用的事件回调设计
  10. ubuntu远程访问摄像头的设置
  11. LINUX系统使用锐捷客户端认证校园网(华中科技大学)
  12. 统计学、机器学习、数据挖掘、深度学习的关系
  13. 蓝鸽智慧计算机教室云服务平台操作说明,蓝鸽VDI云网络教室方案.pdf
  14. 各种统计检验及r语言实现
  15. Oracle Enterprise Linux(OEL) 操作系统下载步骤及版本区别
  16. 移动信息中心号码,移动短信中心号码速查
  17. 《大象:thinking in uml 》(第二版) 11章 系统分析 3-4节 用例实现、软件架构和框架
  18. 软件开发外包:你有什么选择
  19. 如何将彩色文本打印到终端?
  20. pip 查看可安装版本

热门文章

  1. codeforces 938D Buy a Ticket 有初值的Dijkstra、有趣的题目
  2. 2017西安交大ACM小学期数据结构 [线段树]
  3. Hadoop入门(十三)远程提交wordCout程序到hadoop集群
  4. Java集合之EnumSet
  5. jsoup解析HTML用法小结
  6. Maven精选系列--常用命令详情
  7. 如何求解两个数的最大公约数
  8. 购物车的功能——CSS源码
  9. SpringBoot+Vue博客系统---后端接口开发
  10. Redis安装与配置( Windows10 )