版权声明:本文出自胖喵~的博客,转载必须注明出处。

  转载请注明出处:http://www.cnblogs.com/by-dream/p/5569844.html

前言


  学习了上节之后,大家是否已经感觉到Instrumentation的洪荒之力了,的确,比起之前的UI自动化,它可以干很多事情了。但是有的时候呢,可能暴露给你的一些变量,或者函数不够你用,你还是想去调用它的私有函数,或者你直接去重新在测试脚本中组织它的代码,完成和它App相同的功能,这个时候你就需要借助Java的反射机制了。

  另外还是建议学习Instrumentation章节的同学能有一些Java编程基础和Android开发的基础,否则看起来会非常吃力。

概念解释


  既然要用到反射机制,那么先了解下反射机制是什么?

  JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。——摘自**百科《Java反射机制》

  简单通俗的来讲,为什么我们要在测试代码中使用反射呢?是因为我们需要通过反射机制,在拿到一个类对象的时候,去调用它的私有函数,或者是得到使用其私有变量,更复杂的就是调用其私有类对象变量的私有函数。是不是有点懵了?没事,下面我会慢慢的用代码实例讲明白的。

  这里纠结了很久,就是不知道该用什么样的源代码做为讲解的例子,用上一节写的例子吧,有些不太适合,因为基本的方法就可以满足正常的测试了,用反射的话会多此一举。而我当年之所以用到了反射技术,是因为我项目比较特殊,软件需要配合硬件一起使用,而硬件又必须在指定的场景下才能使用,并且项目代码特别的复杂,不是简单的A->B()就能完成一个功能的,里面嵌套着各种组合,聚合等关系,而UI自动化根本不能满足我的测试需求,于是最终选了这条路,但是项目代码涉及保密性又不能在这里贴出来。于是我还是决定用上一节计算器的例子来讲反射吧,只讲反射的用法,至于使用场景,大家只能自己体会了。

反射技术


  因为我们在测试过程中需要的就是拿到私有变量,或者是去调用私有函数。因此我这里直接讲获得方法。

  私有变量

  如果上一节当中的我们在源代码中定义 EditText num1 为 private,且在测试类中不通过findViewById() ,我们使用反射获取,需要如下方式:

   @Override
protected void setUp() throws Exception
{
//...
mainActivity = (MainActivity) getInstrumentation().startActivitySync(intent); // 反射获得 /* 这里num1是mainActivity里面申明的变量,如果是mainActivity父类中的变量,
则可以使用mainActivity.getSuperclass().getDeclaredField("num1") */
/* 这里还有一坑是如果 ***.getClass()获取失败,可以试试Class(**)强转 */
Field fields = mainActivity.getClass().getDeclaredField("num1");
/* 这里必须设置true */
fields.setAccessible(true);
/* 拿到变量 */
num1 = (EditText)fields.get(mainActivity); //...
}

  这样,num1就可以像上一节当中那样使用了。

  私有函数

  上面我们拿到了num1,这个时候我们需要调用它的setText方法,我们假设这个方法是也是私有的,那应该怎么调用呢?看下面:

  mainActivity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
// 反射调用函数,并且传参
try
{
/* 当然自己类内部的也可以使用getDeclaredMethod()*/
Method method = num1.getClass().getMethod("setText", new Class[]{CharSequence.class});
method.setAccessible(true);
/* 这里的返回值就是函数返回值 */
Object object = method.invoke(num1, new Object[] {"21"}); } catch (NoSuchMethodException e){
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
} catch (IllegalArgumentException e){
e.printStackTrace();
} catch (InvocationTargetException e){
e.printStackTrace();
}
//上面代码代替num1.setText("21");
}
});

  这里需要注意的是,一定要使用num做为object去获取函数,另外传参的时候,参数的类型一定得写对,例如这里setText的类型是:

  

  之前就因为一个Int和Integer类型让我查问题查了半天。

  看完这两个用法之后,基本上可以解决所有的问题了,由于当时踩了反射不少的坑,因此自己封装了一些API,有需要的可以自取,源代码贴这里了:

package common;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar; import Result.ResultDataHelper;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
import android.util.Log; public class CommonMethod
{
InstrumentationTestCase main; public CommonMethod(InstrumentationTestCase m)
{
main = m; File picFile = new File(CommonVar.RESULT_PATH);
if (!picFile.exists())
{
picFile.mkdir();
}
} /**
* 根据一个类去找这个类中是否存在一个变量,如果没有就去他的父类中找,直到找到或者找不到
*
* @param cls
* 子类的Class对象
* @param fieldname
* 要找的变量的名字
*
* @return 找到变量的Field 或者是null
*/
public Field GetClassField(Class cls, String fieldname)
{
Field fields = null;
while (cls != null)
{
try
{
fields = cls.getDeclaredField(fieldname);
fields.setAccessible(true);
return fields;
} catch (NoSuchFieldException e)
{
// NoSuchFieldException 异常代表 如果没有找到指定名称的字段。
e.printStackTrace();
cls = cls.getSuperclass();
}
}
BryanLog("没有获取到" + fieldname + "的field", CommonVar.LOG_DEBUG);
return fields;
} /**
* 从任何一个类obj中找到一个名字为fieldname的变量,并返回
*
* @param obj
* 任何类,要找的类
* @param fieldname
* 要找的变量的名字
*
* @return 找到变量的Object 或者是null
*/
public Object GetFileldObjectFromClassObject(Object obj, String fieldname)
{
Object object = null;
Field fields = null; if (obj == null)
{
BryanLog(" 获取 " + fieldname + " ,传递过来的父obj 为null", CommonVar.LOG_DEBUG);
return object;
}
if (obj.getClass() != null)
{
fields = GetClassField(obj.getClass(), fieldname);
}
if (fields != null)
{
try
{
object = fields.get(obj);
//BryanLog("从 "+obj.getClass().getSimpleName() +" 获取 " + fieldname + " 的obj 成功", CommonVar.LOG_DEBUG);
return object;
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
}
}
else
{
BryanLog("从 "+obj.getClass().getSimpleName() +" 获取 " + fieldname + " 的obj 中 obj.getClass() 失败,换了直接拿类的方法", CommonVar.LOG_DEBUG);
// 支持 这样的传参 " 类名.class" ObdFullScreenActivity.class fields = GetClassField((Class)obj, fieldname);
if (fields != null)
{
try
{
object = fields.get(obj);
BryanLog("从 "+obj.getClass().getSimpleName() +" 获取 " + fieldname + " 的obj 成功", CommonVar.LOG_DEBUG);
return object;
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
}
}
}
BryanLog("从 "+obj.getClass().getSimpleName() +" 获取 " + fieldname + " 的obj失败", CommonVar.LOG_DEBUG);
return object;
} /**
* 调用一个类中的私有函数,可以传参
*
* @param obj
* 要调用的函数所在的类,传递类的对象
* @param function
* 函数的名称的字符串
* @param parameterTypes
* 要传递的参数的类型 new Class[]{Intent.class, Integer.class} 也可多个
* @param paramterValue
* 要传递的参数的真实值,需要和上面类型对应new Object[] {intent, i}
*
* @return Object 返回值的对象
*/
public Object CallClassPrivateFun(Object obj, String function, Class[] parameterTypes, Object... paramterValue)
{
Object object = null;
BryanLog("调用 " + obj.getClass().getSimpleName() + " 的 " + function, CommonVar.LOG_DEBUG);
try
{
Class clas = obj.getClass();
Method method;
try
{
method = clas.getMethod(function, parameterTypes);
} catch (NoSuchMethodException e)
{
method = clas.getDeclaredMethod(function, parameterTypes);
}
method.setAccessible(true);
object = method.invoke(obj, paramterValue);
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
} catch (InvocationTargetException e)
{
e.printStackTrace();
} catch (NoSuchMethodException e)
{
BryanLog("没有找到" + function, CommonVar.LOG_DEBUG);
e.printStackTrace();
}
return object;
} /**
* 在UI线程中 调用一个类中的私有函数,可以传参
*
* @param obj
* 要调用的函数所在的类,传递类的对象
* @param function
* 函数的名称的字符串
* @param parameterTypes
* 要传递的参数的类型 new Class[]{Intent.class, Integer.class} 也可多个
* @param paramterValue
* 要传递的参数的真实值,需要和上面类型对应new Object[] {intent, i}
*
* @return Object 返回值的对象
*/
public Object RunOnUITreadCallClassPrivateFun(final Object obj, final String function, final Class[] parameterTypes, final Object... paramterValue)
{
final Object object[] = new Object[1];
try
{
main.runTestOnUiThread(new Runnable()
{
public void run()
{
BryanLog("在ui线程中调用:", CommonVar.LOG_DEBUG);
object[0] = CallClassPrivateFun(obj, function, parameterTypes, paramterValue);
}
});
} catch (Throwable e)
{
e.printStackTrace();
}
return object[0];
} /**
* 从一个Context 对象跳转到另一个Activity,并且返回Activity的对象
*
* @param ctx
* 当前页面的context
* @param cls
* 要找的变量的名字
* @param bundle
* 传递的参数
*
* @return 启动的activity的实例
*/
public Activity startActivityReturnObject(Context ctx, Class<?> cls, Bundle bundle)
{
BryanLog("从 " + ctx.getClass().getSimpleName() + " 调转到 " + cls.getSimpleName(), CommonVar.LOG_DEBUG);
Activity act = null;
Intent intent = new Intent(ctx, cls);
if (bundle != null)
{
intent.putExtras(bundle);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
act = (Activity) main.getInstrumentation().startActivitySync(intent);
return act;
} public String getRunningActivityName(Context context)
{
ActivityManager activityManager=(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
String runningActivity=activityManager.getRunningTasks(1).get(0).topActivity.getClassName();
return runningActivity;
} }

  里面有一些虽然和反射关系不大,但是在Android测试使用反射的过程中,一定也会对你有所帮助的。

【Android测试】【第十七节】Instrumentation——App任你摆布(反射技术的引入)的更多相关文章

  1. Android 高仿腾讯旗下app的 皮肤加载技术

    http://www.cnblogs.com/punkisnotdead/p/4968851.html 以前写的这篇文章 可以高仿出 知乎 新浪微博等 绝大多数app的换肤技术,但是遗漏了腾讯的效果, ...

  2. 【Android测试】UI自动化代码优化之路(临时发布, 随时删除)

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5993622.html 关于UI自动化的抱怨 听过不少人这样 ...

  3. 【Android测试】UI自动化代码优化之路

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5993622.html 关于UI自动化的抱怨 听过不少人这样 ...

  4. 【Android测试】【第十五节】Instrumentation——官方译文

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5482207.html 前言 前面介绍了不少Android ...

  5. 【Android测试】【随笔】获得App的包名和启动页Activity

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5157308.html 前言 经常看到一些刚刚接触Andro ...

  6. 【Android测试】【第九节】MonkeyRunner—— 初识

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4836815.html 不得不说两句,过了这么久才再次更新博 ...

  7. Android测试(一):在Android中测试App

    原文:https://developer.android.com/training/testing/index.html 测试你的App是开发过程中的重要组成部分.通过对应用程序持续的运行测试,你可以 ...

  8. 【Android测试】【第六节】Monkey——认识和使用

    ◆版权声明:本文出自carter_dream的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4688880.html 自动化工具 接触安 ...

  9. 【Android测试】【第一节】ADB——初识和用法

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处.  转载请注明出处:http://www.cnblogs.com/by-dream/p/4630046.html 写在前面的话 感觉自己进入Andr ...

随机推荐

  1. POJ3686 The Windy's(最小费用最大流)

    题目大概说要用m个工厂生产n个玩具,第i个玩具在第j个工厂生产要Zij的时间,一个工厂同一时间只能生成一个玩具,问最少的用时. 这题建的图不是很直观.. 源点向玩具连容量1费用0的边 将每个工厂拆成n ...

  2. POJ3184 Ikki's Story I - Road Reconstruction(最大流)

    求一次最大流后,分别对所有满流的边的容量+1,然后看是否存在增广路. #include<cstdio> #include<cstring> #include<queue& ...

  3. Shell 编程基础之 Until 练习

    一.语法 until [ condition ] # 和while相反,当 condition 条件成立时,就终止回圈, 否则就持续进行回圈的程序段 do #执行内容 done 二.练习 输入用户输入 ...

  4. BZOJ3189 : [Coci2011]Slika

    通过离线将操作建树,即可得到最终存在的操作. 然后逆着操作的顺序,倒着进行染色,对于每行维护一个并查集即可. 时间复杂度$O(n(n+m))$. #include<cstdio> cons ...

  5. Storm DRPC实现机制分析

    DRPC是建立在Storm基本概念(Topology.Spout.Bolt.Stream等)之上的高层抽象,个人理解它的目标是在Storm 集群之上提供一种分布式的RPC框架,以便能够利用Storm快 ...

  6. Node.js学习

    1. 下载 网址:https://nodejs.org/download/ 2. 添加express框架 如下图,运行Node.js command prompt 在命令行中输入:npm instal ...

  7. hadoop 流streaming跑python程序

    先放上命令: hadoop jar /usr/hadoop-/contrib/streaming/hadoop-streaming-.jar -mapper mapper.py -file mappe ...

  8. Codeforces Round #364 (Div. 2) B. Cells Not Under Attack

    B. Cells Not Under Attack time limit per test 2 seconds memory limit per test 256 megabytes input st ...

  9. POJ3461Oulipo 题解

    题目大意: 求字符串A在字符串B中出现的次数. 思路: KMP板题,用Hash也可水过~要学习KMP可参考http://blog.csdn.net/u011564456/article/details ...

  10. Codeforces Round #250 (Div. 2) A. The Child and Homework

    注意题目长度不能考虑前缀,而且如果即存在一个选项的长度的两倍小于其他所有选项的长度,也存在一个选项的长度大于其他选项长度的两倍,则答案不是一个好的选择,只能选择C. #include <iost ...