最近一直在完成一些robotium的小功能,用来更方便的完成一些小功能的测试,或者可以说用来娱乐吧,幸得群内大神思路指点,就此引申,终于把这个功能得以实现

---------------将robotium脚本封装为APK,使用按钮控制用例运行覆盖程度,测试结果以简单的xml文件输入到手机SD卡目录下----------------------

废话不多说,转正题:

一、首先明确一点,这篇文章,是在你所编写的robotium脚本运行无异常的前提下实施

二、阐明思路:

1.我们需要一个运行良好的,逻辑正常的robotium工程

2.我们需要一个可以将脚本用例运行结果保存至本地的方法

3.我们需要一个activity,一个按钮,以及一个按钮点击事件去运行我们的robotium脚本

三、先介绍脚本用例运行结果的功能,我们都知道robotium用例的运行是依赖junit的Instrumentation的,所以,关于结果的输出,需要我们重写InstrumentationTestRunner类

package baih;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import android.content.Context;
import android.os.Bundle;
import android.os.Environment;

public class InstrumentationTestRunner extends
android.test.InstrumentationTestRunner {
    private
Writer mWriter;
    private
XmlSerializer mTestSuiteSerializer;
    private long
mTestStarted;
    private
static final String JUNIT_XML_FILE = "TEST-all.xml";
   
   
   
@Override
    public void
onStart() {
       
try{
           
File fileRobo = new
File(getTestResultDir(getTargetContext()));
           
if(!fileRobo.exists()){
               
fileRobo.mkdir();
           
}
           
if(isSDCardAvaliable()){

File resultFile = new
File(getTestResultDir(getTargetContext()),JUNIT_XML_FILE);
               
startJUnitOutput(new FileWriter(resultFile));
           
}else{
               
startJUnitOutput(new FileWriter(new
File(getTargetContext().getFilesDir(), JUNIT_XML_FILE)));
           
}

}
       
catch(IOException e){
           
throw new RuntimeException(e);
       
}
       
super.onStart();
    }

void
startJUnitOutput(Writer writer) {
       
try {
           
mWriter = writer;
           
mTestSuiteSerializer = newSerializer(mWriter);
           
mTestSuiteSerializer.startDocument(null, null);
           
mTestSuiteSerializer.startTag(null, "testsuites");
           
mTestSuiteSerializer.startTag(null, "testsuite");
       
} catch (Exception e) {
           
throw new RuntimeException(e);
       
}
    }
   
   
    private
boolean isSDCardAvaliable(){
       
return Environment.getExternalStorageState()
                   
.equals(Environment.MEDIA_MOUNTED);

}
   
   
    private
String getTestResultDir(Context context){
       
String packageName = "/" + "robotium";
       
String filepath = context.getCacheDir().getPath() +
packageName;

if(android.os.Build.VERSION.SDK_INT < 8){
           
if(isSDCardAvaliable()){
               
filepath =
Environment.getExternalStorageDirectory().getAbsolutePath()+
packageName;
           
}
       
}else{
           
if(isSDCardAvaliable()){
               
filepath =
Environment.getExternalStorageDirectory().getAbsolutePath()+
packageName;
           
}
       
}

return filepath;
    }
   
    private
XmlSerializer newSerializer(Writer writer) {
       
try {
           
XmlPullParserFactory pf = XmlPullParserFactory.newInstance();
           
XmlSerializer serializer = pf.newSerializer();
           
serializer.setOutput(writer);
           
return serializer;
       
} catch (Exception e) {
           
throw new RuntimeException(e);
       
}

}
   
   
@Override
    public void
sendStatus(int resultCode, Bundle results) {
       
super.sendStatus(resultCode, results);
       
switch (resultCode) {
           
case REPORT_VALUE_RESULT_ERROR:
           
case REPORT_VALUE_RESULT_FAILURE:
           
case REPORT_VALUE_RESULT_OK:
           
try {
               
recordTestResult(resultCode, results);
               
} catch (IOException e) {
                   
throw new RuntimeException(e);
               
}
               
break;
           
case REPORT_VALUE_RESULT_START:
               
recordTestStart(results);
           
default:
               
break;
       
}
    }
   
    void
recordTestStart(Bundle results) {
       
mTestStarted = System.currentTimeMillis();
    }

void
recordTestResult(int resultCode, Bundle results) throws IOException
{
       
float time = (System.currentTimeMillis() - mTestStarted) /
1000.0f;
       
String className = results.getString(REPORT_KEY_NAME_CLASS);
       
String testMethod = results.getString(REPORT_KEY_NAME_TEST);
       
String stack = results.getString(REPORT_KEY_STACK);
       
int current = results.getInt(REPORT_KEY_NUM_CURRENT);
       
int total = results.getInt(REPORT_KEY_NUM_TOTAL);

mTestSuiteSerializer.startTag(null, "testcase");
       
mTestSuiteSerializer.attribute(null, "classname", className);
       
mTestSuiteSerializer.attribute(null, "name", testMethod);

if (resultCode != REPORT_VALUE_RESULT_OK) {
           
mTestSuiteSerializer.startTag(null, "failure");
           
if (stack != null) {
               
String reason = stack.substring(0, stack.indexOf('n'));
               
String message = "";
               
int index = reason.indexOf(':');
               
if (index > -1) {
                   
message = reason.substring(index+1);
                   
reason = reason.substring(0, index);
               
}
               
mTestSuiteSerializer.attribute(null, "message", message);
               
mTestSuiteSerializer.attribute(null, "type", reason);
               
mTestSuiteSerializer.text(stack);
           
}
           
mTestSuiteSerializer.endTag(null, "failure");
       
} else {
           
mTestSuiteSerializer.attribute(null, "time", String.format("%.3f",
time));
       
}
       
mTestSuiteSerializer.endTag(null,
"testcase");

if (current == total) {
           
mTestSuiteSerializer.startTag(null, "system-out");
           
mTestSuiteSerializer.endTag(null, "system-out");
           
mTestSuiteSerializer.startTag(null, "system-err");
           
mTestSuiteSerializer.endTag(null, "system-err");
           
mTestSuiteSerializer.endTag(null, "testsuite");
           
mTestSuiteSerializer.flush();
       
}
    }

@Override
    public void
finish(int resultCode, Bundle results) {
       
endTestSuites();
       
super.finish(resultCode, results);
    }

void
endTestSuites() {
       
try {
           
mTestSuiteSerializer.endTag(null, "testsuites");
           
mTestSuiteSerializer.endDocument();
           
mTestSuiteSerializer.flush();
           
mWriter.flush();
           
mWriter.close();
       
} catch (IOException e) {
           
throw new RuntimeException(e);
       
}
    }
}

重 写InstrumentationTestRunner类后,需要在我们的测试工程下设置run
Configurations,在你的测试工程上右键-移至Run As-run Configurations-android JUnit
Test,更改你工程运行的Instrumentation
runner,如果不更改,默认是android.test.InstrumentationTestRunner

红色框体圈住的位置需要更改为你重写的InstrumentationTestrunner路径

完成以上步骤后,你再运行robotium脚本,就会在你的手机根目录下名为robotium的文件夹中生成一个名为TEST-ALL.xml的文件,用来记录你的脚本运行结果,结构如下:

运行正常的结果,会显示运行时长,运行错误的用例,会打印错误信息,至于错误信息中的中文提示,是我用例中自己加的断言,各位可以自行斟酌

完成这一步,就可以实施我们的封装APK计划了。封装APK,第一,我们需要一个activity

界面比较挫,将就看。关于activity的创建和声明,请各位自行百度

activity中可以放置一个或多个按钮,由于robotium可以使用命令行进行启动运行,所以我们可以使用不同按钮的onclick事件来执行不同的脚本

如上:附上各种命令行启动命令:

运行所有测试用例:

adb shell am instrument -w
packagename/InstrumentationTestRunnername

运行单个测试类或某个TestSuite

adb shell am instrument -e class packagename.testclassname -w
packagename/InstrumentationTestRunnername

运行某个测试类里面的某个测试方法

adb shell am instrument -e class package名.测试类名#方法名 -w
工程名.package名/InstrumentationTestRunner class名

注意:请在编写代码前,在CMD中测试你所使用的命令是否可以正常运行

完成以上步骤后,我们可以将整个测试工程打包为APK文件,记得签名需要签debug签名,然后......安装到你的手机上,拔掉该死的数据线,点击按钮,开始自动虐你的手机吧:)

号外!号外!

由于手头上一直没有android level
17及以上版本的手机,有一个shell命令启动脚本的BUG,发生在SDK level
17及以上

API>=17中加入了INTERACT_ACROSS_USERS_FULL,目的在于允许不同用户的应用之间可以产生交互,了安全,因此在交互时会校验userSerialNumber,,发现用户标识不匹配,导致权限校验失败,就会产生startInstrumentation asks to run as user -2 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL的报错,导致脚本无法调用

群里尝试,发现在17及以上版本,命令中需要加入--user 0参数

adb shell am instrument --user 0 -w
packagename/InstrumentationTestRunnername

可以在调用时使用Build.VERSION.SDK_INT<17来对当前版本做判断选择合适的命令行启动方式

[转载]robotium脚本封装为APK,实现脱离手机数据线,使用按钮点击控制用例的更多相关文章

  1. 将Python脚本封装成exe可执行文件 转

    将Python脚本封装成exe可执行文件 http://www.cnblogs.com/renzo/archive/2012/01/01/2309260.html  cx_freeze是用来将 Pyt ...

  2. 在城市后面加上省,市,区 以及将MySQL入库脚本封装成class

    在城市后面加省,市,区时,使用过滤器和for循环,if判断 一起使用.   自定义一个过滤器 def my_detail(val):                                  ...

  3. Mac-如何安装apk到android手机

    将电脑上的apk安装到手机,Windows系统可以使用usb连接Android手机,然后打开编辑手机中的文件,直接粘贴apk到手机上安装apk.对于Mac来说就没有那么简单啦.那么Mac如何将apk安 ...

  4. 《转载》脚本实现从客户端服务端HTTP请求快速分析

    本文转载自https://www.imooc.com/article/14107 首先我想介绍下,分享这个脚本的用处: 当客户告知我们,一个页面http://www.xxx.com 有问题时,作为PE ...

  5. unity脚本封装成dll

    先申明一下这样做是有需要的.当我们需要把脚本提供给第三方使用,而又不希望对方看到具体的实现过程,这时候就需要将代码封装编译成dll文件,供第三方调用.或是多个项目都要用到同一个模块或同样的功能,则可以 ...

  6. 转载:robotium typeText与enterText区别

    solo.typeText和solo.enterText方法都可以对EditeText进行测试,达到的测试目的是一样的.存在几点不同: 1.实现上,typeText方法是robotium框架调用系统I ...

  7. 转载:Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)

    来源于:http://blog.csdn.net/zhubaitian/article/details/39803857 1. 背景 为保持这个系列的一致性,我们继续用SDK自带的NotePad实例应 ...

  8. [转载]Robotium API 翻译(三)——判断测试结果的方法assert、is、search

    该文来源于:http://blog.csdn.net/dongmu1986   下面的这些方法都主要用来判断测试结果是否与预期结果相符,一般把is和search方法放在assert里面判断.asser ...

  9. <转载> bat 脚本基本语法 http://blog.csdn.net/bluedusk/article/details/1500629

        bat 脚本基本语法 2007-01-25 10:31 常用命令 echo.@.call.pause.rem(小技巧:用::代替rem)是批处理文件最常用的几个命令,我们就从他们开始学起. = ...

随机推荐

  1. Windows 10 下使用Git

    事实上,比在Linux下要难很多.不仅仅是因为Linux下CMD功能较弱,还有就是国内的网络环境,至少,我这Github Windows安装时,总是会下载无法完成 Github Desktop 虽然, ...

  2. 美国L1签证面谈的时候一般VO会问到什么问题?

    L签证:L签证签发给被其中国公司调派到美国分公司或合资公司工作的人员.申请人必须将在美国担任经理级职务或具有专业知识,且在申请签证前的三年中至少为同一雇主或公司连续工作至少一年.签证签发费将因签证的入 ...

  3. 送H-1B 及其他I-129 申请别忘用新表

    (梁勇律师事务所,lianglaw.com专稿)移民局从2010年11月23日 更新了申请H-1B 及其他非移民工作签证I-129 表,从2010年12月23日以后收到的I-129表都必须是2010年 ...

  4. cms系统-帖子页面

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  5. git版本管理工具 标签(Tag) / 版本回退 / 分支的简单使用

    a.标签 标签,可以使用这个功能来标记发布结点. 举个例子, 假如我们的项目版本目前是1.2版本, 上级要求这个版本要在半个月后再进行上传至Appstore, 并要求我们未来的半个月内,去写1.3版本 ...

  6. python_31_集合

    # 集合是一个无序的,不重复的数据组合,它的主要作用如下: # 去重,把一个列表变成集合,就自动去重了 # 关系测试,测试两组数据之前的交集.差集.并集等关系 s = set([3, 5, 9, 10 ...

  7. 《GPU高性能编程CUDA实战中文》中第四章的julia实验

    在整个过程中出现了各种问题,我先将我调试好的真个项目打包,提供下载. /* * Copyright 1993-2010 NVIDIA Corporation. All rights reserved. ...

  8. https及其背后的加密原理阅读总结

    https是以安全为目标的http通道,简单讲是http的安全版.当我们往服务器发送比较隐私的数据(比如说你的银行卡,身份证)时,如果使用http进行通信.那么安全性将得不到保障. 首先数据在传输的过 ...

  9. 【转载】SQLServer中char、varchar、nchar、nvarchar的区别:

    (1)       定义: char:    固定长度,存储ANSI字符,不足的补英文半角空格. nchar:   固定长度,存储Unicode字符,不足的补英文半角空格 varchar:  可变长度 ...

  10. 1074: [SCOI2007]折纸origami

    Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 372  Solved: 229[Submit][Status][Discuss] Descriptio ...