本文出自: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 文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <persons>
- <person id="23" class="a">
- <name>张三</name>
- <age>123</age>
- <sex>0</sex>
- </person>
- <person id="22">
- <name>小红</name>
- <age>25</age>
- <sex>1</sex>
- </person>
- </persons>
然后是实体类:
- package com.test.domain;
- public class Person {
- private Integer id;
- private String name;
- private int age;
- private int sex;
- private String className;
- public String getClassName() {
- return className;
- }
- public void setClassName(String className) {
- this.className = className;
- }
- public int getSex() {
- return sex;
- }
- public void setSex(int sex) {
- this.sex = sex;
- }
- public Person(){}
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- @Override
- public String toString() {
- return this.id+",name:"+this.name+",age:"+this.age+",sex:"+this.sex+",className:"+this.className+"\n";
- }
- }
接下来是我们的handler
- package com.test.dao;
- import java.util.ArrayList;
- import java.util.List;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
- import com.test.domain.Person;
- import android.util.Log;
- public class SaxParserHandler extends DefaultHandler {
- private String tempString;
- private static final String NAMESTRING = "name";
- private static final String AGESTRING = "age";
- private static final String PERSONSTRING = "person";
- private static final String IDSTRING = "id";
- private static final String TAG = "SaxParserHandler";
- private static final String SEXSTRING = "sex";
- private static final String CLASSSTRING = "class";
- private Person person;
- private List<Person> persons;
- private int temp = 0;
- @Override
- public void startDocument() throws SAXException {
- persons = new ArrayList<Person>();
- }
- @Override
- public void endDocument() throws SAXException {
- }
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- //先判断读到的元素是否是person
- if (PERSONSTRING.equals(localName)) {
- // 如果读到的是person这个元素 就要保存起来,保存在我们创建的那个person的类中 所以我们要new一个person类
- person = new Person();
- // attributes是属性。
- Log.i(TAG, "attributes======"+attributes.getValue(temp));
- temp++;
- person.setId(new Integer(attributes.getValue(IDSTRING)));
- if(attributes.getValue(CLASSSTRING)!= null){
- person.setClassName(attributes.getValue(CLASSSTRING));
- }
- }
- Log.d(TAG, "localName======"+localName);
- tempString = localName;
- Log.d(TAG, "tempString======"+tempString);
- }
- /**
- * 在遇到结束标签时调用此方法
- */
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- if(PERSONSTRING.equals(localName)&&person!=null)
- {
- persons.add(person);
- person = null;
- }
- tempString = null;
- }
- /**
- * 当读取xml中文本内容的时候才会调用
- */
- public void characters(char ch[], int start, int length)
- throws SAXException {
- if (person != null) {
- Log.w(TAG, "ch[]+++++++"+ ch);
- String valueString = new String(ch, start, length);
- if (NAMESTRING.equals(tempString)) {
- // 如果当前解析到的节点是name 就要将name中的文本节点元素的值得到
- person.setName(valueString);
- } else if (AGESTRING.equals(tempString)) {
- person.setAge(new Integer(valueString).intValue());
- } else if (SEXSTRING.equals(tempString)){
- person.setSex(new Integer(valueString).intValue());
- }
- }
- }
- public List<Person> getPersons(){
- return persons;
- }
- }
备注:这个地方sax 解析的时候写的不是很规范。。。先将就着 这么用吧。。反正就是i一个demo
然后我们来看service
- package com.test.service;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- import javax.xml.parsers.ParserConfigurationException;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.xml.sax.SAXException;
- import com.test.dao.SaxParserHandler;
- import com.test.domain.Person;
- public class SaxReaderXml {
- public static List<Person> readXml(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException{
- SAXParserFactory factory = SAXParserFactory.newInstance();
- SAXParser saxParser = (SAXParser)factory.newSAXParser();
- SaxParserHandler handler = new SaxParserHandler();
- saxParser.parse(inputStream, handler);
- return handler.getPersons();
- }
- }
最后我们来看测试类:
- package com.test.junittest;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Iterator;
- import java.util.List;
- import javax.xml.parsers.ParserConfigurationException;
- import org.xml.sax.SAXException;
- import android.test.AndroidTestCase;
- import android.util.Log;
- import com.test.domain.ApiIcfBean;
- import com.test.domain.Person;
- import com.test.service.SaxReaderIcfXml;
- import com.test.service.SaxReaderXml;
- public class ParserJunitTest extends AndroidTestCase {
- private static final String TAG = "ParserJunitTest";
- public void testReadXml() throws Exception
- {//类装载器
- InputStream inputStream = ParserJunitTest.class.getClassLoader().getResourceAsStream("component.xml");
- List<Person> personsList = SaxReaderXml.readXml(inputStream);
- for (Iterator iterator = personsList.iterator(); iterator.hasNext();) {
- Person person = (Person) iterator.next();
- Log.i(TAG, person.toString());
- }
- }
- }
。。好了。。这样我们的整个单元测试的代码是 完成了。。但是 还需要一些配置那就是在
清单文件中配置、。。。具体配置如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.test"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk android:minSdkVersion="8" />
- <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.test"/>
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <uses-library android:name="android.test.runner"/>
- </application>
- </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文件添加以下标签元素
- <uses-library android:name="android.test.runner" />
位于<application>元素体内
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- 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输出的所有信息。