这两天在琢磨如何写测试代码。有点感触,在这里慢慢整理出来:
Mock。mock的英文意思是模仿、虚假,在软件测试里面,它代表一种测试手段和思路。跟同事请教了mock的基本用途和使用方法之后,我就觉得把mock描述为一种测试策略更准确点(我也没有系统的学习过测试相关理论,这里也是怎么想怎么说)。EasyMock这样的类库,就是这种策略的一个实现。说明这一点是因为第一次听到另外一个同事介绍Mock时把它解释为一个东西,我了解之后,我觉得不是东西,是模拟被测试类的依赖者而达到测试的目的的一种方法。
对于抽象类,不用单独去测试。实际上也没法测试。直接去测试它的inheritors就可以了。
每个程序员都要明白测试的技术,才能写出可测试的代码。我今天自己给自己前两天写的一个小应用写测试代码,主要是为了学习测试的流程,没写一会,就发现,我之前有些代码写的根本不具备可测试性。我问同事,碰到这种情况怎么办,他说,改实现代码,再接着写测试代码(我们目前没有进行TDD开发,测试代码都是后期补的)。所以我想,既然要去返工,其实也就证明自己写的代码是不合格的,转念一想,如果自己写的代码,某天不是自测,而是他人测试时,被改的“面目全非”,那岂不是很惭愧。因此一定要真正写过测试代码了,再去带着写可测试代码的那种想法去写代码,你这个环节的质量合格,整体质量和效率才会提高。经过长期的练习与实践,当这种想法已经成为编码习惯,那么才有资格说自己是个合格的程序员。
事实上我不得不抱怨Android测试框架,真的很难用。当你使用Android测试api,如果使用错误,这个框架几乎不会给我报告有用的信息让你track问题。就在刚才,我花了几个小时在找一个看起来挺蠢的问题。(见http://code.google.com/p/android/issues/detail?id=8501,碰到这样的问题,真是泪奔)。所以,1.记得给那些你要用到的类加上一个public的无参构造器。2.记得不要开多个ide,然后再运行测试代码。
如何控制和监控Activity的生命周期,以达到测试的目的?有一种做法,就是克隆Activity的
生命周期控制api(题外话:生命周期api,onCreate,onResume等,如果要从设计模式来看,不就是Template Methods吗)。然后定义一个基类Activity实现这些克隆,让克隆api和Activity自由的生命周期api联系起来(让这个基类成为一个facade)。当要测试某个Activity时,只要给基类里的克隆api接口变量注入一个实现实例即可。可能没说清楚,就让代码来解释:
package com.nsworks.testdemo.util;
import android.app.Activity;
import android.os.Bundle;
/**
* The super base activity for all activities, each activity should inherit it.
*/
public class BaseActivity extends Activity {
public interface ActivityLifeCycleController {
public void onCreate(BaseActivity instance, Bundle myCycle);
public void onStart(BaseActivity instance);
public void onResume(BaseActivity instance);
public void onPostResume(BaseActivity instance);
public void onPause(BaseActivity instance);
public void onStop(BaseActivity instance);
public void onDestroy(BaseActivity instance);
}
// Note: this field must be declared static in order to be injected
// in test framework implementations.
private static ActivityLifeCycleController mCycleController;
// Note: Android test framework need each class to have a public constructor
// without any parameters
// I think this is weird, but if a class has not a such constructor will
// cause a failure.
public BaseActivity() {
// Do something
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mCycleController != null) {
mCycleController.onCreate(this, savedInstanceState);
}
}
@Override
protected void onStart() {
super.onStart();
if (mCycleController != null) {
mCycleController.onStart(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mCycleController != null) {
mCycleController.onResume(this);
}
}
@Override
protected void onPostResume() {
super.onPostResume();
if(mCycleController != null) {
mCycleController.onPostResume(this);
}
}
@Override
protected void onPause() {
super.onPause();
if (mCycleController != null) {
mCycleController.onPause(this);
}
}
@Override
protected void onStop() {
super.onStop();
if (mCycleController != null) {
mCycleController.onStop(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mCycleController != null) {
mCycleController.onDestroy(this);
}
}
}
接着,所要做的是在测试环境初始化的过程中加入注入操作:
MemberAccessor.searchSuperField(DayActivity.class, "mDbFacade", new MockCTDbFacade());
看看MemberAccessor工具类的实现:
public static Class<?> searchSuperField(Class<?> target, String fieldName, Object value) throws SecurityException, IllegalArgumentException, IllegalAccessException{
if(target == null)
return null;
if(target.getClass().equals(Object.class))
return null;
Field field = null;
try {
field = target.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException e) {
searchSuperField(target.getSuperclass(), fieldName, value);
}
if(field != null) {
field.set(null, value);
}
return target;
}
注意:被注入的变量一定要是静态的。
这只是思路,提升Activity的可测试性。
给面向接口编程优点之一:提高可测试性。譬如说设计Mock的时候,安逸啊。。
命令行下执行测试代码必备命令:adb shell pm(万恶的google文档,让我按照tutorial折腾了几个小时,也没有弄出来,结果SO上一个回答搞定。。可恶,##)。
如果有命令不明白,去看InstrumentationTestRunner类的doc非常有帮助。
设计良好的intent结构,能提高可测试性。单元测试、Instrumentation测试时,使用intent控制程序流程相当容易,monkey测试时更是如此。其实根据我第一个小demo的经验来看,最容易不写intent的地方就是activity的跳转控制,所以这可能是一个需要注意的地方。
//待续
分享到:
相关推荐
redis 写的操作性能测试代码
卷积神经网络的数字识别手写测试代码 python3.5 准确率98% 取出模型 画板手写测试 字体颜色和背景相差大就行 字体不能太细 有编译代码可以看到数字样本图片
读写内存的测试代码MFC
自己编写的测试代码
(C#语言版)单元测试实例,主要功能包括:(1)输入数据到textbox,以逗号间隔,然后求数组最大值、求和,并将结果显示出来,并针对于数组求最大值函数和求和函数写单元测试代码;(2)连接数据库,写出单元测试代码来测试求...
自己写的测试代码
读写分离插件的源码,其中db-router-test项目是测试项目代码,先 mvn install datasource-router-starter项目后可运行测试;源码解析:https://blog.csdn.net/weixin_47626220/article/details/117090788
德卡D8 读卡器读写CPU卡测试代码
struts2学习测struts2学习测试代码试代码
javamail写的测试代码,打包为jar后可以在服务器上执行测试邮件。javamail写的测试代码,打包为jar后可以在服务器上执行测试邮件
极限编程编程新理念,先测试后写代码,国外提出的新理念,先写好测试在编程
测试的代码 自用 C语言的测试代码 C语言的测试代码
自己写的ssl测试代码
测试APP,教测试代码项目,手机测试编程测试资料文档
片外SRAM的读写操作,本文代码使用的为DE2开发板,IS61LV25616AL 的SRAM进行读写,测试可正常使用
Activiti(测试的代码以及写过的案例)Activiti(测试的代码以及写过的案例)Activiti(测试的代码以及写过的案例)Activiti(测试的代码以及写过的案例)Activiti(测试的代码以及写过的案例)Activiti(测试的代码以及写过的...
socket 代码测试 自己写的一个小的socekt测试文件 包括读取文件能容
c语言写的dpdk测试代码
实际上,写单元测试并不难,也不需要太多技巧,相反,写出可测试的代码反倒是件非常有挑战的事情。所以,今天,我们就再来聊一聊代码的可测试性,主要包括这样几个问题:什
(C#语言版)单元测试实例,主要功能包括:(1)输入数据到textbox,以逗号间隔,然后求数组最大值、求和,并将结果显示出来,并针对于数组求最大值函数和求和函数写单元测试代码;(2)连接数据库,写出单元测试代码来测试求...