Monkey测试
1.Monkey测试的目的
Monkey测试是Android平台自动化测试的一种手段,通过Monkey程序模拟用户触摸屏幕、滑动Trackball、按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常。
2.Monkey测试的执行
通过adb命令开始执行monkey测试,monkey命令的基本格式为:C:\Users\zhenpu.zhang>adbshell monkey-pcom.android.deskclock-v -v -v-s 1000–throttle500 30000>F:\log\monkey_log.txt。这条命令中,-p代表一个包,即我们monkey测试的对象,一条命令可以有多个包,每添加一个包则需要一个-p,如 adb shell monkey –p 包1 –p 包2。com.android.deskclock 就是我们需要执行测试的对象。-v 代表返回结果的详尽程度,分为3级,分别是level 1 level 2 level 3,级别越高,返回的log会更详尽,1级为-v ,2级为-v –v,3级为 –v –v –v.180000 代表执行次数,根据实际测试需要来修改。-s<seed>伪随机数生成器的seed值,它影响伪随机事件的发生顺序,如果用相同的seed值再次执行Monkey,它将产生相同的事件序列。-throttle 500 代表间隔时间,即每次操作的时间间隔。为了更好的模拟用户操作,需要在每次操作之间增加时间间隔,单位是毫秒,此命令的含义就是增加500ms的时间间隔。>F:\log\monkey_log.txt表示monkey的日志将保存在F盘文件夹名为log的文件夹中,日志的文件名为monkey_log.txt。
3.Monkey测试结果
在运行Monkey命令时,遇到crash或者ANR(application not responding),Monkey就会停止运行,Monkey的测试结果可以在monkey_log.txt中看到,如果测试顺利执行完毕,log的最后一行会有Monkey finished,并且手机运行正常。通过多次并且不同设定下的Monkey测试才算它是一个稳定性和健壮性足够的程序。一般我们执行Monkey时,在3万次以内发生Crash的话就认为app是有问题的,要提交PR。MonkeyPR的规则如下,标题:在PR标题上加上Monkey。内容:主要包含自己执行的命令以及在多少次发生crash。其他:在PR上附上相关的Monkeylog以及手机单的后台log,app停止运行的照片也可以贴上。
StrictMode
StrictMode工具类主要用来帮助开发者发现代码中的一些不规范的问题,常用来检查在UI线程中对磁盘的读写与网络的访问,因为在UI线程中进行这样的操作会使得app的反应变得卡顿,有时甚至会弹出ANR对话框。
StrictMode 通过策略方式来让你自定义需要检查哪方面的问题。主要有两中策略,一个时线程方策略(ThreadPolicy),一个是VM方面的策略(VmPolicy)。ThreadPolicy 主要用于发现在UI线程中是否有读写磁盘的操作,是否有网络操作,以及检查UI线程中调用的自定义代码是否执行得比较慢。VmPolicy,主要用于发现内存问题,比如 Activity内存泄露, SQL 对象内存泄露, 资源未释放,能够限定某个类的最大对象数。
如何开启StrictMode
我们通常在 Activity 或者自定义的Application类中启动StrictMode,代码如下:
publicvoidonCreate() {
if (DEVELOPER_MODE){
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for alldetectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
注意:我们只需要在app的开发版本下使用 StrictMode,线上版本避免使用 StrictMode,随意需要通过诸如DEVELOPER_MODE这样的配置变量来进行控制。
至于具体的使用,可以通过下面的例子来展示。
publicclassActivitySimpleextendsActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StrictMode.setThreadPolicy(newThreadPolicy.Builder()
.detectAll()
.penaltyDialog()//弹出违规提示对话框
.penaltyLog()//在Logcat 中打印违规异常信息
.build());
this.testNetwork();
}
privatevoidtestNetwork(){
try{
URLurl=newURL("http://www.baidu.com");
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
conn.connect();
BufferedReaderreader=newBufferedReader(newInputStreamReader(
conn.getInputStream()));
Stringlines=null;
StringBuffersb=newStringBuffer();
while((lines=reader.readLine())!=null){
sb.append(lines);
}
}catch(Exceptione){
e.printStackTrace();
}
}
}
在这里例子中,我们在主线程(UI线程)中执行了网络请求,ThreadPolicy 策略中的 detectAll()
方法 包含而来对这类违规操作的检查,同时我们通过penaltyDialog()
和 penaltyLog()
两个方法将违规信息提示给开发者。
StrictMode.ThreadPolicy.Builder的主要方法:
(1)dtectNetwork()用于检查UI线程中是否有网络访问请求。
(2)detectDiskReads()和detectDiskWrites()检查磁盘的读写。
(3)detectCustomSlowCalls()主要用于帮助开发者发现UI线程调用的那些方法执行得比较慢,要和 StrictMode.noteSlowCall
配合使用,StrictMode.noteSlowCall
只有通过 StrictMode.noteSlowCall
用来标记“可能会”执行比较慢的方法,只有标记过的方法才能被检测到,日志中会记录方法的执行时间(比如 ~duration=2019 ms)。当然你也可以在其他线程中使用detectCustomSlowCalls(),但是没有什么实际意义,也看不到方法执行时间。
(4)penaltyDeath(),当触发违规条件时,直接Crash掉当前应用程序。
(5)penaltyDeathOnNetwork(),当触发网络违规时,Crash掉当前应用程序。
(6)penaltyDialog(),触发违规时,显示对违规信息对话框。
(7)penaltyFlashScreen(),会造成屏幕闪烁,不过一般的设备可能没有这个功能。
(8)penaltyDropBox(),将违规信息记录到 dropbox 系统日志目录中(/data/system/dropbox),你可以通过如下命令进行查看:
adb shell dumpsys dropbox dataappstrictmode --print
(9)permitCustomSlowCalls()、permitDiskReads ()、permitDiskWrites()、permitNetwork: 如果你想关闭某一项检测,可以使用对应的permit*方法。
VMPolicy的常用方法:
(1)detectActivityLeaks() 用户检查 Activity 的内存泄露情况。
public class ActivityTestActivityLeaks extends Activity {
private static boolean isStrictMode= false;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(! isStrictMode){
StrictMode.setVmPolicy(new VmPolicy.Builder()
.detectActivityLeaks()
.penaltyLog()
.build());
isStrictMode = true;
}
new Thread() {
@Override
public voidrun() {
while (true){
SystemClock.sleep(1000);
}
}
}.start();
}
}
(2)detectLeakedClosableObjects()和detectLeakedSqlLiteObjects(),资源没有正确关闭时会触发。detectLeakedSqlLiteObjects() 和detectLeakedClosableObjects()的用法类似,只不过是用来检查SQLiteCursor 或者 其他 SQLite 对象是否被正确关闭。
public classMainActivityTestDetectLeakedClosableObjects extendsActivity {
private static boolean isStrictMode= false;
@Override
protected voidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(! isStrictMode){
StrictMode.setVmPolicy(new VmPolicy.Builder()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
isStrictMode = true;
}
File newxmlfile = newFile(Environment.getExternalStorageDirectory(),"aaa.txt");
try {
newxmlfile.createNewFile();
FileWriter fw = newFileWriter(newxmlfile);
fw.write("aaaaaaaaaaa");
//fw.close(); 我们在这里故意没有关闭 fw
} catch (IOExceptione) {
e.printStackTrace();
}
}
}
(3)detectLeakedRegistrationObjects() 用来检查 BroadcastReceiver 或者 ServiceConnection 注册类对象是否被正确释放。
public class ActivityTestLeakedRegistrationObjects extends Activity {
private TextView textView= null;
private static boolean isStrictMode= false;
private MyReceiverreceiver = null;
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.textView = (TextView)findViewById(R.id.text_view);
this.textView.setText("In ActivityTestLeakedRegistrationObjects");
if(! isStrictMode){
StrictMode.setVmPolicy(new VmPolicy.Builder()
.detectLeakedRegistrationObjects()
.penaltyLog()
.build());
isStrictMode = true;
}
this.receiver = newMyReceiver();
IntentFilter filter =new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST");
registerReceiver(this.receiver, filter);
}
@Override
protected voidonDestroy() {
super.onDestroy();
}
}
(4)setClassInstanceLimit(),设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露。
public class ActivityTestObjectLimit extends Activity {
private static classMyClass{}
private static boolean isStrictMode= false;
private staticList<MyClass> classList = new ArrayList<ActivityTestObjectLimit.MyClass>();
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(! isStrictMode){
StrictMode.setVmPolicy(new VmPolicy.Builder()
.setClassInstanceLimit(MyClass.class,2)
.penaltyLog()
.build());
isStrictMode = true;
}
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
classList.add(new MyClass());
}
}