android 单元测试

来源:互联网 发布:鲱鱼罐头 知乎 编辑:程序博客网 时间:2024/04/28 07:20

本文出自:http://blog.csdn.net/haojunming/article/details/6951124

很早一段时间之前我在做web项目的时候就接触过单元测试,因为那是后项目用到spring 而spring 中很重要的一点就是测试驱动 开发。。。。这一点真的很重要,在web项目几乎是每一个类或者功能模块都有对应的测试用例!这样就提高了程序的质量从而在实际运行当中减少了程序出现bug 的几率! 

现在也做了 这么长时间的android 开发了。单元测试也接触了一些,但是之前的话旨在工作中使用 并没有好好地回忆或者记录以及收录一下,以便于自己以后能更好的运用。那么今天就好好地 
回忆一下吧。。(主要是因为现在项目也不是很紧,有点打酱油了。。) 

好了入正题: 

android 单元测试的编写其实不难但是,方式有很多而且针对不同的android 模块(我通常吧 activity 叫做view service 以及 后台的处理 则成为 service 或者 dao)也有不用的测试基类,其实仔细想想觉得 测试真麻烦。。。擦擦的! 

那么我们就按照模块来做测试吧。。 

先看看dao层。。比如我们通常要处理的什么 网络连接了。。。从服务端通过http下载的数据了。。(反正返回的是流。。我们只测试流就可以了),以及数据库的存储了。。还有 什么文件的保存了。之类 只要和 service 或者 activity 无关的我们都可以通过 继承这样 一个基类 
那就是AndroidTestCase来进行单元测试。。。 

下面我给出一些自己的通过sax解析xml的 demo:(正好大家也可以学习一下sax 解析了。。哈哈) 
xml 文件如下: 

Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <persons>    
  3.     <person id="23" class="a">    
  4.         <name>张三</name>    
  5.         <age>123</age>    
  6.         <sex>0</sex>  
  7.     </person>    
  8.     <person id="22">    
  9.         <name>小红</name>    
  10.         <age>25</age>  
  11.         <sex>1</sex>    
  12.     </person>    
  13. </persons>    


然后是实体类: 
Java代码  收藏代码
  1. package com.test.domain;  
  2.   
  3. public class Person {  
  4.     private Integer id;    
  5.     private String name;    
  6.     private int age;  
  7.     private int sex;  
  8.     private String className;  
  9.     public String getClassName() {  
  10.         return className;  
  11.     }  
  12.     public void setClassName(String className) {  
  13.         this.className = className;  
  14.     }  
  15.     public int getSex() {  
  16.         return sex;  
  17.     }  
  18.     public void setSex(int sex) {  
  19.         this.sex = sex;  
  20.     }  
  21.     public Person(){}  
  22.     public Integer getId() {  
  23.         return id;  
  24.     }  
  25.     public void setId(Integer id) {  
  26.         this.id = id;  
  27.     }  
  28.     public String getName() {  
  29.         return name;  
  30.     }  
  31.     public void setName(String name) {  
  32.         this.name = name;  
  33.     }  
  34.     public int getAge() {  
  35.         return age;  
  36.     }  
  37.     public void setAge(int age) {  
  38.         this.age = age;  
  39.     }    
  40.     @Override  
  41.     public String toString() {  
  42.         return this.id+",name:"+this.name+",age:"+this.age+",sex:"+this.sex+",className:"+this.className+"\n";  
  43.     }  
  44. }  

接下来是我们的handler 
Java代码  收藏代码
  1. package com.test.dao;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import org.xml.sax.Attributes;  
  7. import org.xml.sax.SAXException;  
  8. import org.xml.sax.helpers.DefaultHandler;  
  9.   
  10. import com.test.domain.Person;  
  11.   
  12. import android.util.Log;  
  13.   
  14. public class SaxParserHandler extends DefaultHandler {  
  15.   
  16.     private String tempString;    
  17.     private static final String NAMESTRING = "name";    
  18.     private static final String AGESTRING = "age";    
  19.     private static final String PERSONSTRING = "person";    
  20.     private static final String IDSTRING = "id";  
  21.     private static final String TAG = "SaxParserHandler";  
  22.     private static final String SEXSTRING = "sex";  
  23.     private static final String CLASSSTRING = "class";  
  24.     private Person person;  
  25.     private List<Person> persons;  
  26.     private int  temp = 0;  
  27.   
  28.     @Override  
  29.     public void startDocument() throws SAXException {  
  30.         persons = new ArrayList<Person>();  
  31.     }  
  32.   
  33.     @Override  
  34.     public void endDocument() throws SAXException {  
  35.       
  36.     }  
  37.     public void startElement(String uri, String localName, String qName,  
  38.             Attributes attributes) throws SAXException {  
  39.         //先判断读到的元素是否是person    
  40.         if (PERSONSTRING.equals(localName)) {    
  41.             // 如果读到的是person这个元素 就要保存起来,保存在我们创建的那个person的类中 所以我们要new一个person类    
  42.             person = new Person();    
  43.             // attributes是属性。    
  44.             Log.i(TAG, "attributes======"+attributes.getValue(temp));  
  45.             temp++;  
  46.             person.setId(new Integer(attributes.getValue(IDSTRING)));   
  47.             if(attributes.getValue(CLASSSTRING)!= null){  
  48.                 person.setClassName(attributes.getValue(CLASSSTRING));  
  49.             }  
  50.         }     
  51.         Log.d(TAG, "localName======"+localName);  
  52.         tempString = localName;    
  53.         Log.d(TAG, "tempString======"+tempString);  
  54.     }  
  55.   
  56.     /** 
  57.      * 在遇到结束标签时调用此方法 
  58.      */  
  59.     public void endElement(String uri, String localName, String qName)  
  60.             throws SAXException {  
  61.         if(PERSONSTRING.equals(localName)&&person!=null)    
  62.         {    
  63.             persons.add(person);    
  64.             person = null;    
  65.         }    
  66.         tempString = null;    
  67.     }  
  68.     /** 
  69.      * 当读取xml中文本内容的时候才会调用 
  70.      */  
  71.     public void characters(char ch[], int start, int length)  
  72.             throws SAXException {  
  73.          if (person != null) {   
  74.                 Log.w(TAG, "ch[]+++++++"+ ch);  
  75.                 String valueString = new String(ch, start, length);    
  76.                 if (NAMESTRING.equals(tempString)) {    
  77.                     // 如果当前解析到的节点是name 就要将name中的文本节点元素的值得到    
  78.                     person.setName(valueString);    
  79.                 } else if (AGESTRING.equals(tempString)) {    
  80.                     person.setAge(new Integer(valueString).intValue());    
  81.                 } else if (SEXSTRING.equals(tempString)){  
  82.                     person.setSex(new Integer(valueString).intValue());  
  83.                 }   
  84.          }    
  85.     }  
  86.       
  87.     public List<Person> getPersons(){  
  88.         return persons;  
  89.     }  
  90. }  

备注:这个地方sax 解析的时候写的不是很规范。。。先将就着 这么用吧。。反正就是i一个demo 

然后我们来看service 
Java代码  收藏代码
  1. package com.test.service;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.util.List;  
  6.   
  7. import javax.xml.parsers.ParserConfigurationException;  
  8. import javax.xml.parsers.SAXParser;  
  9. import javax.xml.parsers.SAXParserFactory;  
  10.   
  11. import org.xml.sax.SAXException;  
  12.   
  13. import com.test.dao.SaxParserHandler;  
  14. import com.test.domain.Person;  
  15.   
  16.   
  17. public class SaxReaderXml {  
  18.   
  19.     public static List<Person> readXml(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException{  
  20.         SAXParserFactory factory = SAXParserFactory.newInstance();  
  21.         SAXParser saxParser = (SAXParser)factory.newSAXParser();  
  22.   
  23.         SaxParserHandler handler = new SaxParserHandler();  
  24.           
  25.         saxParser.parse(inputStream, handler);  
  26.           
  27.         return handler.getPersons();  
  28.     }  
  29. }  


最后我们来看测试类: 

Java代码  收藏代码
  1. package com.test.junittest;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.util.Iterator;  
  6. import java.util.List;  
  7.   
  8. import javax.xml.parsers.ParserConfigurationException;  
  9.   
  10. import org.xml.sax.SAXException;  
  11.   
  12. import android.test.AndroidTestCase;  
  13. import android.util.Log;  
  14.   
  15. import com.test.domain.ApiIcfBean;  
  16. import com.test.domain.Person;  
  17. import com.test.service.SaxReaderIcfXml;  
  18. import com.test.service.SaxReaderXml;  
  19.   
  20.   
  21. public class ParserJunitTest extends AndroidTestCase {  
  22.   
  23.     private static final String TAG = "ParserJunitTest";  
  24.     public void testReadXml() throws Exception    
  25.     {//类装载器    
  26.         InputStream inputStream = ParserJunitTest.class.getClassLoader().getResourceAsStream("component.xml");    
  27.             
  28.         List<Person> personsList = SaxReaderXml.readXml(inputStream);    
  29.         for (Iterator iterator = personsList.iterator(); iterator.hasNext();) {    
  30.             Person person = (Person) iterator.next();    
  31.             Log.i(TAG, person.toString());    
  32.         }    
  33.     }  
  34. }  


。。好了。。这样我们的整个单元测试的代码是 完成了。。但是 还需要一些配置那就是在 

清单文件中配置、。。。具体配置如下: 

Java代码  收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="com.test"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.     <uses-sdk android:minSdkVersion="8" />  
  7.     <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />    
  8.     <instrumentation  
  9.     android:name="android.test.InstrumentationTestRunner"   
  10.     android:targetPackage="com.test"/>  
  11.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  12.         <uses-library android:name="android.test.runner"/>  
  13.     </application>  
  14. </manifest>  


。。好了到这里位置。。我们已经可以做单元测试了。。。。 

但是这里你看到的只仅仅是做dao 层得单元测试。。 

/******************************接下来我们说说activity 和service的单元测试*******************************************/ 

那么接下来我们说说activity 和service的单元测试 

当我们在给你 ui层 做单元测试的时候我们得实现ActivityInstrumentationTestCase2<T> 

里面的T就是你要测试的那个activity类的名字。。。 

在这里我们要 从写 这个ActivityInstrumentationTestCase2 基类的setup方法  。。。 

在这个里面我们需要把测试 所用到的 组件(比如 button  view 之类的东西)在具体的测试方法之前给实例化了。。 


然后才是我们相对应的 testMethod 。。。 


同理 service 的测试则是实现ServiceTestCase<T extends Service>。。。。 他和activity 的测试 相同。。这里不给出详细的解释了。。。。 

其实除了这个创建测试的方式之外我们月还有另外一种方式。。但是这种方式的话是我们从新创建一个测试工程。在你创建android project的时候你可以看到。。。有一个androidtestproject 项目的工程。。。。 

就是它了。。在创建的过程中你只需要吧要测试的项目 给 build 进入这个测试工程就行了 




Step1:向AndroidManifest.xml文件添加以下标签元素

view plain
  1. <uses-library android:name="android.test.runner" />  


位于<application>元素体内

view plain
  1. <instrumentation android:name="android.test.InstrumentationTestRunner"  
  2.         android:targetPackage="测试类包名" android:label="Tests for My App" />  


与<application>元素并列

Step2:新建单元测试类,必须继承AndroidTestCase,此外测试类中的测试方法需要以"test"+"待测试方法的名称"(比如testSendData测试的就是sendData方法)

Step3:方法要throws Throwable异常,Throwable是Exception的父类,单元测试框架捕获Throwable。

Step4:调用测试,在Outline面板或方法名上右键—Run AS—Android Junit Test。

Step5:查看打印信息,可以事先使用Log类中的方法来打印信息(用法参见Log图文详解(Log.v,Log.d,Log.i,Log.w,Log.e)的用法),打开 LogCat面板,在这个面板中我们可以看到Android输出的所有信息。