System.out.println()方法可以在控制台打印信息,在Android平台下如何
进行单元测试和信息输出呢?我对单元测试的用途还不太了解,博士您能给
我讲讲吗?
大致:那接下来我带你学习如何对Android应用进行单元测试及日志输出吧。
使用单元测试可以保证我们开发应用的质量,一般我们开发完业务层代码之
后应该对业务层进行单元测试,确保业务层不出现Bug。对业务层测试通过之
后,控制层就可以调用业务层完成需要的功能。另外,单元测试会加快我们
开发的速度。如果不进行单元测试,我们需要将应用发布到模拟器上之后,
再对应用中的各项功能进行测试,这样效率较低。(本书后面的许多例子都会
进行单元测试,希望能够掌握单元测试的方法)
1 单元测试与日志输出
1.1单元测试步骤
进行单元测试需要以下几步:
第一步:首先在AndroidManifest.xml 中加入下面粗体代码:
android:versionCode=\"1\"android:versionName=\"1.0\"> ....
说明:
该行代码必须位于 上面targetPackage 指定的包要和应用的package 相同。如果不相同,会出现找不到单 元测试用例的错误。 第二步:编写单元测试代码 测试类必须继承自AndroidTestCase 类 第三步:执行测试。 1.2单元测试示例 1. 创建项目 创建名为“UnitTestAndLog”的项目,如下图。 需要注意的是,由于我们在本项目中并不需要使用Activity,因此我们可以不创 建Activity,只需要将【Create Activity】前面的单选框去除选定就可以不用输入 Activity 名称。如果去除选定,在项目包下就不会创建Activity。当然你也可以输入 一个名称,虽然我们不会用到。 2. 在功能清单文件中配置单元测试的环境 打开功能清单文件,添加上面所述的代码,最终代码如下。 AndroidManifest.xml android:versionCode=\"1\" android:versionName=\"1.0\"> android:icon=\"@drawable/icon\" android:label=\"Tests for My App\" /> 3. 新建测试用例 首先,在项目包位置,右键单击之后点击【New】【Class】,如下图所示。 点击【Class】之后到如下界面。填入【Name】信息为MyTest。 下面的Superclass 填写的方式如下,点击其后面的【Browse】,界面如下。 在【Choose a type】下面的文本框中输入“androidtestcase”,点击【OK】。之后 再点击【Finish】完成测试用例的创建工作。在AndroidTestCase 测试中,我们采用的Junit3,因此测试方 法的书写格式是: public void testMethodName(){ } 初学者常犯的错误时为测试方法添加参数。一定要注意测试方法是不需要传入参数的。 如果测试方法内部遇到异常,建议直接抛出,而不要捕获异常。异常抛出后会被测试框架获取,之后在控制台显示出来,方便我们了解异常信息。 测试类内容如下: MyTest.java package com.sharpandroid.unitandlog; import junit.framework.Assert; import android.test.AndroidTestCase; import android.util.Log; public class MyTest extends AndroidTestCase { private static final String TAG = \"MyTest\"; public void testSave() throws Throwable{ int i = 4+8; Assert.assertEquals(5, i); } } Assert 类在此的作用是判断所得到的结果与期望值的关系。这里判断i 是否等于5;如 果相等则通过,不相等,则抛出异常。 4. 运行测试用例 首先,进入“大纲视图”(Outline),如果Eclipse 中没有该视图,可用如下图之方式 打开. 打开之后,其位置可能不太合意,可以用鼠标拖动它的标题栏到指定的位置。其他的视 图也可采用拖动的方式拖动到其他位置上去。 现在可以运行测试用例了。右键单击,之后【Run As】【Android JUnit Test】,操作如下图所示。 运行完成,我们观察Junit 的控制台。结果如下图。 如上图,结果条显示为红色,说明测试没有通过。 下面将测试用例中的代码修改为“Assert.assertEquals(12, i);”之后再次运行。 如图,结果条显示为绿色,说明测试通过。 1.3日志输出 前面已经提到Console 控制台只能输出应用安装的信息。比如在程序中添加一行 System.out.println(“sharpandroid”); 如果在Eclipse 中进行Java 程序开发,该语句会在Console 中打印。但我们开发Android应用时,该语句在Console 控制台中并不会打印出字符串。该字符串会在LogCat 中打印出来。 在Android 程序中进行信息输出,一般采用android.util.Log 类的静态方法就可以实现。Log 类所输出的日志的内容从少到多分别是ERROR、WARN、INFO、DEBUG、VERBOSE,对应五种不同类型的首字母,分别对应有Log.e()、Log.w()、Log.i()、Log.d()、Log.v()五种静态方法,使用不同的方法输出的信息的颜色各不相同,并且如下图,在Logcat 控制台右上侧有相应的按钮右侧类型的日志信息。如点击信息则不会显示。 ,,点击每个按钮,可以过滤出其自身类型及其 按钮,会显示I、W、E 三种类型的信息,而V、D 类型的 1.编写日志输出测试类 修改后的代码如下: package com.sharpandroid.unitandlog; import junit.framework.Assert; import android.test.AndroidTestCase; import android.util.Log; public class MyTest extends AndroidTestCase { private static final String TAG = \"MyTest\"; public void testSave() throws Throwable{ int i = 4+8; Log.i(TAG, \"i = \" + i); Assert.assertEquals(12,i); } } 说明: . Log.i(String tag, String msg); tag : 为这条信息定义一个标签,在开发时通常采用其所在类的类名,这样方便我们追踪输出的信息。也方便我们在看到信息时知道其由哪个类输出。 msg :该参数为希望输出的信息的内容。一般将第一个参数定义成一个静态常量,按照JAVA 语言的规范,静态常量的命名一般全部采用大写。书写大写字母时不太习惯的,可以先书写为小写字母,再将其选中,在Eclipse中使用“Ctrl+Shift+X”快捷键将字符串变成大写。使用“Ctrl+Shift+Y”恰好相反,将英文字符串变成小写。 第二步执行测试 执行的方法如同单元测试一节的执行测试方法。 打开日志信息查看器“LogCat”,查看是否输出该信息。打开步骤如下图: 选择【LogCat】后,点击【OK】,此时观察Eclipse 下方,出现LogCat 控制台,如下图。 我们会发现里面输出的内容非常多,要找到我们在程序中打印的那句字符串比较困难, 在这里可以添加一个过滤器,来选取我们指定标记的信息。其中标记为我们在调用 Log.i(TAG, \"i = \" + i)中指定过的TAG,本例中即为“MyTest”。添加过滤器方法如下。 点击上图中的“+”,弹出添加Log Filter 界面,在其中填写信息如下图: 点击【OK】。在LogCat 管理器中多出一个选项卡“MyTest”,在该选项卡中出现我们在代码中想要打印的信息。 下面介绍下LogCat 管理器中几个按钮的功能。 点击上图中的“V”、“D”、“I”、“W”、“E”,可以过滤出当前选项卡中的对应级别的信息。如在Log 选项卡下点击W 键后结果如下图。 另外,LogCat 控制台右侧有另外三个图标,其对应含义如下: 编辑过滤器删除过滤器清除日志信息 2揪出程序中的臭虫——程序调试示例 大致:小安,前面咱们已经做三个项目了,我现在给你出一道题目,做一个 项目,项目的目标很简单,一个加法器,两个EditText用于输入两个数字, 点击一个Button之后在另一个EditText中显示结果。怎么样?有没有信心 搞定? 小安:小菜一碟,您等着吧,我立马搞定。嘿嘿! 下面是小安实现的全程实录。 1. 创建工程 创建一个名为“DebugDemo”的工程,如下图: 2. 编写main.xml: 3. 编写DebugActivity.java: package com.sharpandroid.debug; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class DebugActivity extends Activity { private EditText num1Edit ; private EditText num2Edit ; private TextView resultText; private Button calButton ; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); num1Edit = (EditText) findViewById(R.id.num1); num2Edit = (EditText) findViewById(R.id.num2); calButton = (Button) findViewById(R.id.calbutton); calButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String num1Str = num1Edit.getText().toString(); String num2Str = num2Edit.getText().toString(); String result = getResult(num1Str, num2Str); resultText.setText(result); } }); } public String getResult(String num1Str,String num2Str){ float num1 = Float.parseFloat(num1Str); float num2 = Float.parseFloat(num2Str); float result = num1 + num2; return \"\"+result; } } 做完这些,小安信心十足,非常高兴,认为大致博士怎么搞这么简单的题目,自己这么快就搞定了,心中不免暗自得意。于是执行程序,效果如下图: 这个情况让小安大跌眼镜了,怎么出现这么一个弹出框呢?以为是系统的Bug呢,点击 【强行关闭】,之后又重复执行了几遍,谁知道Android一点面子都不给,还是坚持给了这么个提示。把小安给急的满头大汗,心想这么简单的程序怎么就出错了呢?这可如何是好? 只好向大致博士求救。 小安:博士,这是怎么了?弹出这么个玩意儿?什么意思啊? 大致:我看看啊,哎呀,小安呐,你这肯定是程序中什么地方写错啊。别急别急,由 于经验不足或者一些疏忽,在开发的道路上总会遇到这样或那样的问题,即便是一些具有多年开发经验的老手也会犯一些错误。初学者往往会被程序中出现的错误弄得焦头烂额、手足无措,你也经常在论坛或者QQ群里见有初学者发一些“十万火急、跪求大侠指点迷津”等求救的帖子吧?其实在本博士的法眼里多数错误是非常简单和低级的,是非常容易发现的,只是这些发帖的朋友缺少一种能力,一种成为真正程序员所必备的可谓安身立命的在这个行业闯荡的基本本领,这就是——调错能力。犯了错误并不可怕,错误是难免的,问题是要及时发现错误修正错误,伟大的领袖曾经说过知错能改还是好同志。因而如何发现错误、如何改正错误就非常重要,接下来我传授你一套绝招,教你如何利用Eclipse工具跟踪整个程序的运行状况以及对错误的调试及纠正,让你如同福尔摩斯般的侦查出程序中的蛛丝马迹,找到程序的bug,成为一个高手! 下面是小安在大致指导下的操作流程。 1. 首先,检查了main.xml文件,仔细观察之后发现小安将EditText标签,误写成EditView了,于是全部修改为EditText。并且输入框没有添加内容限制,这样不仅会在输入框中输入数字,也有可能输入汉字或者其他非数字型字符,因此在前两个输入框中添加了一个属性,指定输入的字符必须为小数(decimal)或整数(integer),语句如下:android:numeric=\"decimal\" android:numeric=\"integer\" 另外,显示结果的那个输入框不需要用户进行操作,因此可以将其设置为不获得焦点, 语句如下:android:focusable=\"false\" 修改后的main.xml 文件如下: android:layout_width=\"fill_parent\" android:layout_height=\"fill_parent\" > android:layout_height=\"wrap_content\" android:numeric=\"decimal\" android:id=\"@+id/num1\" /> android:layout_height=\"wrap_content\" android:text=\"@string/add\" /> android:layout_height=\"wrap_content\" android:numeric=\"integer\" android:id=\"@+id/num2\" /> android:layout_height=\"wrap_content\" android:text=\"@string/equ\" /> android:layout_height=\"wrap_content\" android:focusable=\"false\" android:id=\"@+id/result /> 2. 之后再次运行程序。结果如下图: 3. 在前两个输入框分别输入10 和15,点击【计算】按钮。效果如下图: 仍然有错误,但是此时界面已经没有错误,接着,开始对Java 代码进行排错。 4. 首先为代码设置断点。断点的作用就是当调试程序的时候,程序执行到该行代码时会停止执行,等待开发人员的处理。设置断点的方法很简单,在Eclipse 中的某行代码左侧(编辑器边框上)双击即可。将断点设置在OnCreate()方法的第一行代码的位置,这样可以观察整个执行流程。如下图的位置: 5. 设置完断点之后,可以开始调试程序。需要注意的是调试程序和运行程序的方法有所区别。调试程序的启动方法是右击项目名,选择【Debug As】【Android Application】。 如下图: 点击之后,观察模拟手机,显示如下界面,该界面询问用户是否进行调试,如果点击 【Force Close】,则不进行调试,也不执行程序。因此我们不要点击【Force Close】按钮,片刻之后该提示就会自动消失。 此时模拟器界面如下: 除了标题栏之外,屏幕上没有任何内容显示。而此时在电脑下方的状态栏上Eclipse 开 发的状态条上显示为橙色,如下图: 6. 点击查看出现什么状况,Eclipse 弹出如下提示,询问是否打开Debug 透视图。 7. 点击【Yes】,提示框消失,弹出Debug 视图。如下图: 观察中间的代码窗口,程序将要执行的行会以不同的背景色显示并且在其左侧会有一个 的箭头指向该行,如下图: 8. 想要程序继续执行,可以点击标题为Debug 的那一栏的第二个 按钮中的 按钮,或者使用快捷键F6,其作用是单步跳过,执行到下一行代码。如下图: 程序执行到下一行,其下一行的背景色变化,并且移至该行。 9. 继续点击按钮或者快捷键F6。点击多次,直到程序执行到一个陌生的类,在Eclipse 中间代码框上的标题为, 此时代码已经执行到了 Android 框架为我们提供的处理代码,我们不必理会,此时应点击 中的第三个按钮或者快捷键F7,作用是从该方法中返回。一直点击该按钮或者F7 直到界面不发生变化为止,此时代码框上选项卡为,也即程序 目前在Looper 类内停止。 在此之前模拟器屏幕没有任何变化,为上面的空界面。此时再查看模拟器,发现我们的 布局文件已经在屏幕上显示。如下图: 10. 如前,在输入框中输入数字,并点击【计算】按钮。界面无变化,进入Eclipse 中查看。点击F7,直到没有变化为止。查看界面,仍然提示错误,如下图: 11. 点击【强行关闭】。模拟器退回主页,如下图: 12. 点击Eclipse 右上方的按钮,之后点击【Java】,如果没有【Java】选项,选择【Other】,之后再选择【Java】,进入Java 视图,如下图: 13. 之前的程序我们已经测试没有错误,错误应该就在点击按钮所触发的事件的代码中,因此我们更改断点的位置,在onClick()方法内第一行代码处双击添加断点。在上一个super.onCreate(savedInstanceState);这一行前面的断点双击,取消该断点。如下图: 14. 重复上面第5 步的方式以【Debug As】的方式执行调试过程。 在屏幕中已经显示出界面,如前,在输入框中输入数字,并点击【计算】,进入Eclipse 中查看,程序执行到了第13 步所设的断点,如下: 15. 点击F5,直到指向“String result = getResult(num1Str, num2Str);”一行。如下图: 16. 此时点击 中的第一个按钮 或者快捷键F5,作用是单步跳入 该行具有的方法,即getResult()方法内部。点击F5 后,程序执行到getResult()方法 内第一行,效果如下图: 17. 点击F6,直到指向该方法最后一行即Return 语句时停止。 我们可以查看已经执行过的代码中的变量的值,只需将鼠标在该变量上悬浮即可弹出提示框,如下图。 18. 另外在Eclipse 右上角的Variables 栏中也有已经执行过的代码行中的变量值。如 下图。 19. 继续点击F6,或者F7。又指向“String result = getResult(num1Str, num2Str);” 一行。点击F6,到下一行“resultEdit.setText(result);”。再次点击F6,进入 类中。一直点击F7 直到没有变化为 止。此时查看模拟器,又提示错误,点击【强制关闭】。由于是执行到“resultEdit.setText(result);”之后出错的,现在可以定位错误就在该行。 去除其他断点,将断点设定在该行,如下图: 20. 按照第5 步的方法,以调试方式执行程序。在模拟器界面中填入数字,点击【计 算】。 进入Eclipse 中,目前指向“resultEdit.setText(result);”一行。选中resultEdit 这个变量,右击鼠标,选择【Watch】,如下图: 查看右上角的栏,栏中信息如下: 发现resultEdit=null。说明之前没有给resultEdit 赋值,该对象为空,在Java 中调用空对象的方法会发生一种常见的异常——空指针异常。这时错误已经找到,我们只需要在使用resultEdit 对象之前为其添加赋值语句即可。点击Debug 栏的,表示执行程序直 到下一个断点为止,由于已没有其他断点,程序直接执行完毕。 21. 另外,大致博士对小安说,为代码添加注释是一个良好的习惯,于是为代码添加了详细的注释信息。 修改后的DebugActivity.java 代码如下: package com.sharpandroid.debug; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class DebugActivity extends Activity { private EditText num1Edit ; private EditText num2Edit ; private TextView resultEdit; private Button calButton ; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //得到相关对象 num1Edit = (EditText) findViewById(R.id.num1); num2Edit = (EditText) findViewById(R.id.num2); // 少写的一行代码,为resultEdit 对象赋值。 resultEdit = (TextView) findViewById(R.id.result); calButton = (Button) findViewById(R.id.calbutton); //为计算按钮添加单击监听器 calButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取加数和被加数的字符串值 String num1Str = num1Edit.getText().toString(); String num2Str = num2Edit.getText().toString(); //调用getResult()方法计算结果 String result = getResult(num1Str, num2Str); //将结果在resultEdit 上显示 resultEdit.setText(result); } }); } //将传入的两个字符串解析成float 类型,并求他们的和之后返回和。 public String getResult(String num1Str,String num2Str){ //将第字符串解析成float 类型 float num1 = Float.parseFloat(num1Str); float num2 = Float.parseFloat(num2Str); //求它们的和 float result = num1 + num2; return \"\"+result; } } 22. 执行程序,输入数字,点击【计算】,效果如下: 此时已正确显示结果。程序调试通过。 小安终于完成任务。 小安:博士,你太厉害了,要不是您我就不知道怎么办了,里面的错误还真不少,我以后一定小心。 大致:刚开始都这样,出错不可怕,只要能把错误调出来就ok了,调试错误的方法有很多种,我刚才教你的只是最基本和常用的,其他的你可要自己慢慢摸索了。加油了,小伙子! 小安:我一定继续努力,早日成为高手!呵呵。 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务