android编程基础(零基础学习Android开发第五课)

视频:《零基础学习Android开发》第五课 类与面向对象编程1-3

单元测试、Debug

六、单元测试与Debug

1. 单元测试

  前面我们在敲代码的时候,为了输出结果都是用的在图形界面上显示文字结果的方法,这个方法缺点比较明显,要对界面的组件进行设置,点击“运行”以后反应时间也很长,因为除了编译之外还要启动模拟器将程序安装到模拟器以后再运行。在学习了类的基础知识后,我们可以使用单元测试来快速测试代码是否能够实现预期功能。

  我们在左侧项目项目工具窗口中逐级点开“app>java>com.example.helloworld(test)”可以看到其下有一个ExampleUnitTest类文件,打开以后文件内容如下:

import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 2); } }

  对上面代码解读如下:

  1. 前两条语句是引入测试类的包,以后照抄就行。
  2. 主体是一个类,类名是可以任取的,建议取为要测试的类加Test结尾的形式。
  3. 起到测试作用的是加了“@Test”注解(英文是Annotation)的方法,在测试运行后该方法内的代码就会运行。可以在方法内加入对想测试的类的实例化与调用方法的代码。判断测试是否成功则是靠assert开头的测试方法,assert是“断言”,assertEquals就是断言相等的意思。把调用方法的返回结果与期望的值进行对比,如果相等就说明被测试的方法运行是正常的,否则说明存在bug需要修改。

  我们写一个对Pokers的测试类,如下:

public class PokersTest { @Test public void testGetNextPoker() { Pokers p = new Pokers(); assertNotEquals(p.getNextPoker(), p.getNextPoker()); } }

  这段代码测试了Pokers类的getNextPoker方法,因为这副是被洗过的,不存在两张相同的牌,因此使用assertNotEquals方法断言两次调用的值不相等。要运行单元测试,我们在左侧项目项目工具窗口右击PokersTest,在弹出菜单中选择“Run 'PokersTest'”,运行结果在调试窗口中显示如下:

android编程基础(零基础学习Android开发第五课)(1)

  使用单元测试有以下经验:

  1. 一个测试类里可以写多个测试方法(注意都要加@Test注解),运行时可选择单独运行某个测试方法(右击方法名,从弹出菜单中选择“Run 方法名”),也可以运行类中所有测试方法(在项目工具窗口中右击类名选择“Run 类名”),还可以运行所有测试类(在项目工具窗口中右击Test文件夹选择“Run Test in 项目名”)。同时运行多个测试方法的好处是测试新代码对原有代码的改变,提前发现关联性Bug。
  2. 断言方法有许多种,同学们可以键入assert后从联想菜单中可以看到,大家可以自己试试。
  3. 除了@Test注解的测试方法,单元测试中还可以用@开头的多种注解,其中常用的是@Before与@After,分别是在启动测试前与与测试结束后被调用,可以@Before注解的方法里进行一些初始化行为,在@After注解方法里可以做一些信息归纳。这些注解的方法名称都没有特殊要求,只要符合标识符规范都可以。
  4. 除了使用断言方法判断测试成败之外,还可以使用一些输出语句来获得更多信息,比如使用System.out.print方法与println方法(前一个不换行,后一个换行)。

  我们可以修改上面的测试类,修改后代码如下:

public class PokersTest { @Before public void init(){ System.out.println("before"); } @After public void finish() { System.out.println("after"); } @Test public void testGetNextPoker() { Pokers p = new Pokers(); int a = p.getNextPoker(), b = p.getNextPoker(); System.out.print("第一次调用值为:" a); System.out.println(",第二次调用值为:" b); assertNotEquals(a, b); } }

  运行结果显示在调试窗口中,如下:

android编程基础(零基础学习Android开发第五课)(2)

  Android Studio提供自动生成测试类的辅助功能。我们以Player类来演示,在编辑器窗口中右击Player类的代码,在弹出菜单中选择“Generate...”,在弹出的菜单中再选择“Test...”,弹出如下对话框。

android编程基础(零基础学习Android开发第五课)(3)

  在这个对话框里,已经自动生成测试类名,在Generate部分,有setUp/@Before与tearDown/@After两个多选项就是分别生成带@Before与OAfter注解的两个方法,而且方法名称会分别取为setUp与tearDown。在Generate test methods for部分勾选上要生成为它生成测试方法的方法。点击“OK”,会弹出一个新的对话框。

android编程基础(零基础学习Android开发第五课)(4)

  这个对话框是选择生成测试类所在的文件夹。这里有两个选项,androidTest是有android类库支持的,我们创建的类中如果没有依赖android类库就选择下面的test文件夹即可。点击“OK”即自动生成了Player类的测试类代码。如果后续在该测试类中增加对其它方法的测试,进行按这个步骤进行即可。

2. Debug

  同学们,我们讲了这么久的课,今天终于要讲到程序员的真正工作内容了。大家认为程序员每天真正在干是什么吗?是写代码吗?错!是制造Bug,有一张图片很好地概括了程序员的生活。

android编程基础(零基础学习Android开发第五课)(5)

  我们终于开始要Debug了,就是把代码中存在的错误找出来。进行Debug的第一步就是要会设置断点(英文是Breakpoint)。断点设置在怀疑存在问题的代码段开始之处,设置方法是在编辑窗口的左侧行序号之后单击,单击处会出现一个红色的圆点。如下图所示就在测试方法内设置了一个断点。

android编程基础(零基础学习Android开发第五课)(6)

  设置了断点后,点击工具栏绿色小虫的Debug按钮,即开始Debug运行。

android编程基础(零基础学习Android开发第五课)(7)

当代码运行到设置断点处时,程序会暂停,编辑器变为如下状态,

android编程基础(零基础学习Android开发第五课)(8)

代表断点的红色圆点会出现一个勾,表示程序当前运行到这里。而调试窗口也发生了变化,显示内容如下:

android编程基础(零基础学习Android开发第五课)(9)

在该窗口的中间位置是Variables变量窗口,显示了当前位置作用域有效的变量,可以逐个点击展开,就能看到更底层变量的值。展开变量p后,可以看到其成员变量数值的所有值。如下图所示:

android编程基础(零基础学习Android开发第五课)(10)

调试窗口上部的工具栏中比较常用的是下面这组运行控制按钮

android编程基础(零基础学习Android开发第五课)(11)

从左往右,第一个是Show Execution Point,显示执行点,点击该按钮后光标将定位到当前正在调试的位置。第二个是Step Over,单步跳过,当前代码有方法调用时执行完该调用后到下一行,不进去方法内部。第三个是Step Into,单步跳入,当前代码有方法调用时会进入方法内部。第四个是Fore Step Into,强制单步跳入,前一个Step Into只能进入本项目创建的方法内部,而它能跳入引用的外部类的方法中。第五个是Step Out,单步跳出,是一直运行到跳出当前调用的方法。第六个是Drop Frame(没有好的翻译,直译是丢帧,我认为可以翻译为闪回),与Step Out类似也会跳出当前运行的方法,但并不运行完后续代码而是回到调用该方法之初的状态,以再次开始方法调用。第七个是Run to Cursor,运行至光标处,它会使程序运行跳过中间设置的断点,直至运行到光标所在处。

  我在课程中介绍的都是Debug中常用的一些功能,同学们可以自己多试一试,积累一些经验。更多的用法与技巧,同学们如果在实践中遇到问题时再找找资料,这里就不多说了。

  再给大家介绍一下Debug的一点经验。程序出现bug是难免的,关键是要早发现,不要把小bug积成大bug,对于bug要抓早抓小。如何让bug还是小bug的时候被发现呢?诀窍就是多使用单元测试。有一种开发模式叫“测试先行”,就是在做一个类之前先想好这个类的方法该如何测试,方法头一确定就给它写好测试方法。方法一写好就开始测试,而且是对类所有的方法都同时测试。这样的好处是不仅可以测试当前的方法,抓出刚写完的代码里可能的bug。还可以测试类中所有的方法,因为在写代码的时候难免会调整其它部分的代码,如果调整出了错这时也能很快发现。这是第一条经验。

  第二条经验是如果测试暴露出了问题,说明存在了bug,想要准确定位bug所在的位置,办法就是回到上次测试成功后代码改变的地方,再开始Debug。变化一定带来风险,风险就起源于变化开始的地方。

  再给大家讲一个程序员的笑话:说的是有一位软件工程师、一位硬件工程师和一位项目经理同坐车参加研讨会。不幸在从盘山公路下山时坏在半路上了。于是两位工程师和一位经理就如何修车的问题展开了讨论。硬件工程师说:“我可以用随身携带的瑞士军刀把车坏的部分拆下来,找出原因,排除故障。”项目经理说:“根据经营管理学,应该召开会议,根据问题现状写出需求报告,制订计划,编写日程安排,逐步逼近,alpha测试,beta1测试和beta2测试解决问题。”软件工程师说:“咱们还是应该把车推回山顶再开下来,看看问题是否重复发生。”

  同学们,今天我们学习了面向对象的第一课,面向对象编程是编程基础知识里理论多、难点多的部分,很多人容易被一大堆概念绕进去。但我在这个课程里更愿意从实用的角度来讲面向对象。我们就把类看成一个代码的组织方式,它是数据类型的升级版。别的数据类型中只有数据,而类则把与数据相关的方法也封装了起来,形成了一个非常好用的结构。正是这样的一个调整,使得类成为一个自管理的组织,使得它更接近于社会实际运行的模型,也方便了程序设计与后期的维护调整。我们把这次课的内容总结如下:

  1. 类的组成部分,包括类声明、成员变量、构造方法与成员方法。提到了各种访问修饰符,但是没有详细讲解,这个留到下次课再讲。
  2. 创建类的对象与对象的使用。
  3. 面对对象编程基本概念。.......
  4. 面向对象五大基本原则我们讲解了单一职责原则。
  5. 讨论了类的实例成员与静态成员的区别。
  6. 介绍了包的概念及如何引入其它包中的类。
  7. 单元测试给了我们一个快速且轻量级的测试方法,能随时通过测试来发现代码中可能出现的错误。“测试先行”是开发时值得提倡的习惯。
  8. Debug是开发工作的重要部分,IDE为Debug提供了许多有用的工具。
,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页