Android异步接口测试

标签: 测试理论 自动化测试 Android 异步接口 | 发表时间:2012-12-14 16:10 | 作者:百度质量部
出处:http://qa.baidu.com/blog

 
    基于Android的C/S移动应用中访问后端数据的场景是非常多的,异步接口测试主要是在单元测试完成的基础上检查接口级访问是否正确,主要保证对外请求的组装与发送是否符合后端的约定。现在项目的异步接口访问都遵循一个特定的访问模式:前台的Activity获取到触发事件后将接受到的参数传给一个异步任务,这些任务大都是AsyncTask的实现——即启动一个新的线程访问后台接口数据,完毕后调用回调函数更新UI展示,示意图如下:

 
一. 测试框架
 
对于Android中这种异步接口的自动化测试需要解决3个问题:
1) 如何获取到异步任务执行结果;
2) 如何让上层测试代码尽量不处理任务等待;
3) 如何处理需要登录的接口。
对于问题1)每个异步任务在获取结果后就直接调用onPostExecute()方法了,测试代码获取不到结果,所以必须有一个专门的桩Activity负责异步任务的执行并将结果暴露出来;
对于问题2)尽量将等待操作交给测试基类,上层测试代码只需要执行被测逻辑而不需要关心细节;
对于问题3)采用模板模式,如果接口需要登录则先执行登录操作后再调用,整体解决方案如下:
 

先介绍桩Activity,它负责异步任务的实际调用,同时承担分发器的作用,如果需要登录则先分发给需要登录的流程,不需要登录则直接调用不需要登录的流程。当然它还有重要作用就是取到异步接口调用后的结果,这样Test Case获取到并比对结果,示意代码如下:
 

可以看到isCompleted是标识异步任务是否执行完毕的,无论异步任务返回是onSuccess、onError还是onIOException都会进行置位;result则是异步调用的返回,可以看到这里无论接口调用是成功、失败还是io异常都会将这个结果暴露出来以使测试代码能够获取到。
然后介绍InterfaceAction,它是测试代码需要实现的接口,内容如下:
 

前两个接口实现是为其服务的,如果访问的接口必须处于登录状态则让neddLogin()返回true同时实现login()的逻辑,不需要登录则直接让neddLogin()返回false即可。而action()是核心测试逻辑,包括发送数据的准备和实际接口的调用。
最后介绍的是测试用例都需要继承的测试基类,他主要为了减少对异步任务等待的代码以及显示对InterfaceActivity这个桩Activity的调用,示意代码如下:
 

这样上层test case只需要关心具体的测试逻辑而不用关心异步调用及等待处理,只需要取到返回的result进行断言。
 
二. 测试代码示例
 
    首先介绍一下测试用例的组织形式,一共分为两层:action层和test case层,action层为异步调用逻辑层,所有的类都实现InterfaceAction,case层为测试用例层,主要组装各种action并断言结果,如下图所示:

为了使示例能够简单明了地表达意图,假设我们要测试Bar接口(需要登陆),BarAction是实际操作,BarTest是测试用例:
 

这里BarAction继承于BaseLoginAction,它默认实现了needLogin()和login()方法,这样BarAction本身就不必关心登录逻辑了。当然更好的写法是“common”和“bar”这些测试数据从params中取,这样就可以从测试用例层直接传递测试数据过来。接下来是BarTest的实现:
 

测试类主要是对BarAction的调用,它可以向接口传递不同的参数,同时也可传递登录信息。asyncInvoke(action)完成了接口逻辑的调用及时间的等待,测试代码只需着重关注result并进行断言。
 
三. 总结
 
    异步接口的集成测试的侧重点在于Android手机端向服务器端发送的请求是否正确,以上测试Case的断言与后端数据其实是强耦合的,即后端bar这个账户的数据变化可能导致Case的fail,所以可以考虑引入hamcrest包,做一些匹配校验,主要测试正常和异常情况服务器返回的内容是否符合预期,比如上面最后一个断言可以写成:assertEquals(resp.getResult() (), greaterThan(0));当然,服务器端接口的正确性正常情况下应该由服务器端的自动化Case来保证,这样才不至于前后端测试紧耦合在一起。
 
 
 
 

相关 [android 异步 接口] 推荐:

Android异步接口测试

- - 百度质量部 | 软件测试 | 测试技术 | 百度测试
    基于Android的C/S移动应用中访问后端数据的场景是非常多的,异步接口测试主要是在单元测试完成的基础上检查接口级访问是否正确,主要保证对外请求的组装与发送是否符合后端的约定. 现在项目的异步接口访问都遵循一个特定的访问模式:前台的Activity获取到触发事件后将接受到的参数传给一个异步任务,这些任务大都是AsyncTask的实现——即启动一个新的线程访问后台接口数据,完毕后调用回调函数更新UI展示,示意图如下:.

Android handler异步更新

- - 博客园_首页
private static final int MSG_SUCCESS = 0;// 获取图片成功的标识. private static final int MSG_FAILURE = 1;// 获取图片失败的标识. mImageView.setImageBitmap((Bitmap) msg.obj);// imageview显示从网络获取到的logo.

Android 视频缩略图的缓存机制和异步加载

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 在这次的工作开发项目中,涉及到一个视频缩略图的视频列表;这个在大家看来,制作视频缩略图就是两行代码就搞定的事. 确实是这样的,百度一下,每个 帖子都知道制作视频缩略图的方法,在这里确实也是一样的,但是我要实现的是让缩略图 在ListView上显示,而且不会出现卡顿,其实也不是特别难,确实很实用;.

获取Android各类系统相关信息的接口实现代码

- - CSDN博客移动开发推荐文章
* 获取系统中所有安装包信息. // 这里可以控制是否需要获取系统级别的package. String cpuInfo=""; //1-cpu型号 //2-cpu频率. String mtype = android.os.Build.MODEL; // 手机型号. String numer = mTm.getLine1Number(); // 手机号码.

Android 异步获取网络图片并处理图片Out Of Memory 内存溢出问题

- - 博客园_首页
测试环境为Adnroid 2.1以上. 1:AndroidManifest.xml 权限配置:. 2.异步图片类 ImageDownloadTask . * 这里获取到手机的分辨率大小. 如:http://www.xxxxxx.com/xx.jpg. //这3句是处理图片溢出的begin( 如果不需要处理溢出直接 opts.inSampleSize=1;).

教你如何更省流量刷空间,微博---Android之异步加载网络图片

- - 编程语言 - ITeye博客
      朋友你是否有过这样的经历,当你刷空间,刷微博的时候,有的时候那些图片加载要很长时间,有的却直接可以看到无需慢慢等待,尤其是那些你已经刷过的内容,即使你处于断网的状态下也能看到,往往这种客户体验相对而言比较好,但是有的时候我们清理了手机的一些垃圾后,就不一样了,我们还得从新刷出来,下面我就要说说这个原理了.

Android性能优化之实现双缓存的图片异步加载工具(LruCache+SoftReference) - 拿来即用

- - CSDN博客推荐文章
之前在郭大神的博客看到使用LruCache算法实现图片缓存的.这里仿效他的思路,自己也写了一个. 并加入ConcurrentHashMap>去实现二级缓存,因为ConcurrentHashMap是多个锁的线程安全,支持高并发.很适合这种频繁访问读取内存的操作..

iOS 免费接口

- - ITeye博客
京东获取单个商品价格接口:. http://p.3.cn/prices/mgets?skuIds=J_商品ID&type=1. ps:商品ID这么获取:http://item.jd.com/954086.html. http://www.kuaidi100.com/query?type=快递公司代号&postid=快递单号.

linux异步IO浅析

- Sepher - kouu's home
知道异步IO已经很久了,但是直到最近,才真正用它来解决一下实际问题(在一个CPU密集型的应用中,有一些需要处理的数据可能放在磁盘上. 预先知道这些数据的位置,所以预先发起异步IO读请求. 等到真正需要用到这些数据的时候,再等待异步IO完成. 使用了异步IO,在发起IO请求到实际使用数据这段时间内,程序还可以继续做其他事情).

异步上传文件

- - Web前端 - ITeye博客
通过iframe来实现无刷新的的文件上传,其实是有刷新的,只是在iframe里面隐藏了而已. form里面的target要与iframe里面的id的值相等,指示是form相应了post事件,也就是post时间相应的时候刷新的是iframe而不是整个页面. 用户名:
上传头像: