Android平台Robotium UI测试详解
来源:互联网 发布:sql注入绕过安全狗 编辑:程序博客网 时间:2024/05/01 14:33
Android平台Robotium UI测试详解
Robotium 框架工作原理及实践
Robotium 是什么
一款面向Android端的开源自动化测试框架,,Robotium是基于Instrumentation的测试框架,器测试用例的编写框架是基于Junit的
- 优势
- 同时支持Native应用和Hybrid(混合)
- 支持黑盒测试和白盒
- 基于Instrumentation,测试用例执行速度快
- 运行时识别的是控件,测试用例更健壮
- 可以通过Maven,Gradle或Ant运行
- 可以没有项目代码,只有APK文件
- 可以截图
- 缺点
- 无法跨应用
- 代码运行在被测进程,会影响性能,无法同时监控性能
Robotium 的Native使用
关于Eclipse的使用在Robotium的GitHub主页中可以找到文档,以下均是基于AndroidStudio的
要完成对手机的模拟操作,应包含以下几个基本操作:
1. 需要知道所要控件的坐标
2. 对要操作的控件进行模拟操作
3. 判断操作完成后的结果是否符合预期
1.使用 Robotium 准备工作
在需要测试modle中添加依赖
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.3'
AndroidStudio 在创建项目的时候,会默认的创建出androidTest路径,如果没有的话,需要在想要测试的module中的src文件夹下创建一个androidTest/java的包,然后配置module的build.gradle来指向它
android { sourceSets { androidTest { java.srcDirs = ['androidTest/java'] } }}
- 然后就可以写测试代码了,在java中建一个和项目根包名相同的包,我们带代码都放到这里面
2.简单的例子
写一个简单的求和功能
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.lanou.chenfengyao.robotiumdemo.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/num1_et" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="a"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+"/> <EditText android:id="@+id/num2_et" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="b"/> <TextView android:id="@+id/result_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="=结果"/> </LinearLayout> <Button android:id="@+id/get_result_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="求和"/></LinearLayout>
MainActivity代码
package com.lanou.chenfengyao.robotiumdemo;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private EditText num1Et, num2Et; private Button getResultBtn; private TextView resultTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); num1Et = (EditText) findViewById(R.id.num1_et); num2Et = (EditText) findViewById(R.id.num2_et); getResultBtn = (Button) findViewById(R.id.get_result_btn); resultTv = (TextView) findViewById(R.id.result_tv); getResultBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int a = Integer.valueOf(num1Et.getText().toString()); int b = Integer.valueOf(num2Et.getText().toString()); //故意写错了 resultTv.setText("= " + a + b); } }); }}
可以看到点击按钮的时候就会求和,并写回TextView上,可以看到我们的求和结果是故意写错了,直接这么写会是字符串的拼接而不是求和的,我们会通过测试代码来找到这个错误
测试代码-1
在androidTest/java/包名下新建一个Java class,开始写我们的测试代码,测试代码类需要继承ActivityInstrumentationTestCase2,这是测试单独Activity时需要继承的泛型填写我们需要测试的Activity类名,接下来是代码
import android.test.ActivityInstrumentationTestCase2;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.robotium.solo.Solo;/** * If there is no bug, then it is created by ChenFengYao on 2017/3/3, * otherwise, I do not know who create it either. */public class TestFirst extends ActivityInstrumentationTestCase2<MainActivity> { private Solo solo; public TestFirst() { super(MainActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); solo = new Solo(getInstrumentation()); getActivity(); } public void testRun() { EditText num1Et = (EditText) solo.getView(R.id.num1_et); EditText num2Et = (EditText) solo.getView(R.id.num2_et); Button resultBtn = (Button) solo.getView(R.id.get_result_btn); TextView resultTv = (TextView) solo.getView(R.id.result_tv); solo.enterText(num1Et,"1"); solo.enterText(num2Et,"2"); solo.clickOnView(resultBtn); solo.sleep(200); assertEquals("= 3",resultTv.getText().toString()); }}
这段代码我们复写了setUp方法,在setUp方法里我们初始化了Robotium中最重要的对象,Solo对象,几乎所有对于UI的操作都是针对这个Solo对象的,然后通过调用getActivity来启动MainActivity
下面 是testRun方法,这个方法并不是重写出来的,而是就写成这个名字,写成testXX的都能被识别,在testRun里,通过solo.getView方法来获得界面中我们所有需要的View元素,然后通过solo.enterText对EditText进行赋值,接下来调用button的点击事件,值得注意的是,测试代码并 不是运行在主线程中 的,所以我们没有办法直接操作view元素来进行更改ui,也正因为这样,我们在点击过后让solo等待一下,方便让点击事件生效,最后开始断言,textview中的文字是否是我们期望的文字,如果是的话,就证明我们的程序是没有问题的.
点击类名左边的绿色箭头就可以直接运行,也可以在运行项目那选择测试用例点击运行按钮
可以看到程序运行后自动的填写了数组,并点击了确定,AndroidStudio的控制台也自动变成到了测试的界面,而测试页面也出现了错误日志
第一句就告诉了我们 我们期待的结果是 = 3,而 出现的结果是 = 12,我们把程序代码改正再来运行一下
这回出现了我们期待的结果,测试就通过了
测试代码-2
Android也提供了通过注解的形式来运行测试代码,要比之前的形式更优雅一点
import android.support.test.InstrumentationRegistry;import android.support.test.rule.ActivityTestRule;import android.support.test.runner.AndroidJUnit4;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.robotium.solo.Solo;import org.junit.After;import org.junit.Before;import org.junit.Rule;import org.junit.Test;import org.junit.runner.RunWith;import static org.junit.Assert.assertEquals;/** * If there is no bug, then it is created by ChenFengYao on 2017/3/3, * othrwise, I do not know who create it either. */@RunWith(AndroidJUnit4.class)public class TestFirst{ private Solo solo; @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class); @Before public void setUp() throws Exception { solo = new Solo(InstrumentationRegistry.getInstrumentation(), activityTestRule.getActivity()); } @Test public void testRun() { EditText num1Et = (EditText) solo.getView(R.id.num1_et); EditText num2Et = (EditText) solo.getView(R.id.num2_et); Button resultBtn = (Button) solo.getView(R.id.get_result_btn); TextView resultTv = (TextView) solo.getView(R.id.result_tv); solo.enterText(num1Et,"1"); solo.enterText(num2Et,"2"); solo.clickOnView(resultBtn); solo.sleep(200); assertEquals("= 3",resultTv.getText().toString()); } @After public void tearDown() throws Exception { solo.finishOpenedActivities(); }}
效果与之前的测试代码是一样的,就不额外演示了,与之前的代码不同,我们想要获得Activity对象则需要通过@Rule注解来获得,@Before和@After都是junit框架提供的,代表测试代码运行前和运行后,而测试方法则需要是用@Test注解进行修饰
Robotium的Native使用
控件的获取
Robotium中获取控件主要有两大方式:
- 根据被测应用的控件ID来获取
- 先获取当前页面所有的控件,对这些控件进行过滤封装后再提供相应获取控件的API
1. 根据控件的ID来获取
EditText num1Et = (EditText) solo.getView("num1_et");EditText num2Et = (EditText) solo.getView(R.id.num2_et);
在Android中,所有的控件都会继承View,所以该方法能获取项目中所有的View,如果被测试的应用中控件有唯一ID的话,是一定能通过这种形式来获取所要操作的控件的,获取View之后再向下转型成我们需要的类型即可, 当View拥有唯一ID的时候,优先使用这种方式
2. 根据控件类型的索引,文本来获取
根据索引获取
Robotium会先将当前界面中所有控件全部获取,然后按控件类型,索引进行过滤后获取指定的控件//获取页面中第一个类型是Button的控件Button button = (Button) solo.getButton(0);
而基本上常用的控件Robotium都会提供这样的API,例如EditText,TextView等都是这样,但是如果是自定义组件就不行了
根据文本获取控件
//返回界面中text属性是"登陆"的ButtonButton loginBtn = (Button) solo.getButton("登陆");
这种方法则局限性更大,只能找到具有text属性的一些控件
根据控件类型获取控件
Robotium可以获取 页面中/指定View下 所有的控件,或同一类型的控件//获取当前页面或dialog中所有的控件ArrayList<View> getCurrentViews();//获取当前页面或Dialog中所有类型为classToFilterBy的控件ArrayList<T> getCurrentViews(Class<T> classToFilterBy);//获取父控件parent下所有控件类型为classToFilterBy的控件ArrayList<T> getCurrentViews(class<T> classToFilterBy, View parent);
获取相同id的控件
在遇到ListView这种组件的时候,要想对item内部的View进行操作会发现这些View无论是Id还是文本还是类型都是一致的,而ListView本身是可以很方便的获得这些item的,最简单的可以通过listView.getChildAt(index)的形式,而在获取到Item之后,我们又可以通过调用findViewById来获取该Item下的组件了//先获取ListViewListView listView = (ListView) solo.getView("main_lv");//获取指定的itemView convertView = listView.getChildAt(0);//从指定item中获取指定viewTextView itemTv = (TextView) listView.findViewById(R.id.item_tv);
这种通过findViewById的方式来获取控件,也适用于从Activity等页面中来获取指定控件.
注意:
- 当没找到想要的控件时,Robotium就会抛出运行时异常,我们可以手动捕获该异常来防止测试代码崩溃
- 尽量不适用通过索引的方式获得控件,这种方式过于依赖页面的布局结构
控件操作
对Android端控件的操作大概有点击,长按,文本输入,滑动,滚动,截图等,而为了页面真实性,有时还需要加入等待等操作
注意:
- Robotium 对控件的操作是不能够跨进程的,所以它是不能够点击到例如通知栏等区域,如果编写了这样的代码,程序是会崩溃的
- 在调试时建议打开 “指针位置” 功能,这样在编写测试代码的时候,就可以知道点击的位置坐标了
1. 点击,长按
点击和长按操作可以是找到组件然后点击该组件,而如果是自定义View时根据位置去处理手势的话,还可以根据指定的坐标去点击或者长按
//点击/长按指定的View控件void clickOnView(View view);void clickLongOnView(View view);//点击/长按指定的屏幕坐标void clickOnScreen(float x, float y);void clickLongOnScreen(float x, float y);
Robotium 还提供了点击文本,图片的api,例如 clickOnButton(String text),它会先根据text找到Button,然后再去点击这个Button,目前Robotium也提供了点击RecyclerView的item的方法clickInRecyclerView(int itemIndex)这样的方法,来方便对ListView,RecyclerView进行操作
2. 输入操作
//在指定EditText中输入textvoid enterText(EditText editText, String text);//在指定EditText中键入文本void typeText(EditText editText, String text);//清空指定的输入框void clearEditText(EditText editText);
clearEditText比较好理解,而enterText和typeText的区别是enter是直接对EditText进行赋值,而type则是像用户一样的一个一个文本的输入
EditText editText = (EditText) solo.getView(R.id.main_et);solo.enterText(editText, "editText");
3. 滑动,滚动操作
//从起始x,y坐标滑动至终点x,y坐标,通过stepCount参数指定滑动时的步长void drag(float fromX, float toX, float fromY, float toY, int stepCount);//滚动到顶部/底部void scrollToTop();void scrollToBottom();//向上滚动屏幕/向下滚动屏幕void scrollUp();void scrollDown();//滚动至ListView第line行void scrollListToLine(AbsListView absListView, int line);
根据坐标法进行滑动的时候坐标不需要多说,而步长的意思是滑动的速度,例如从A点滑动到B点,如果步长为1,那么将直接产生从A到B的滑动手势,如果步长为100,则会将A到B之间均分成100份,然后依次滑动
滚动到顶部/底部的方法是将当前屏幕滚动到顶部或底部,什么能滚,滚什么,例如:如果是ListView就会直接滚动到ListView的顶部,如果是WebView就会滚动到WebView的顶部
向上滑/向下滑则也是根据当前屏幕控件来走的,基本上是滑动一屏的距离,而与drag不同的是,这些都是调用控件自身的api来进行滚动的,例如drag能触发下拉刷新等操作,但是scroll则不能,所以drag更贴近用户的操作
对新的RecyclerView,同样提供了Api,但是不能指定滑动到第几行,只有向上/向下滑动,和滚动到顶端/底端,并且也只能通过RecyclerView的索引来找到RecyclerView
boolean scrollUpRecyclerView(index);boolean scrollDownRecyclerView(index);boolean scrollRecyclerViewToTop(index);boolean scrollRecyclerViewToBotton(index);
4. 搜索与等待
UI自动化测试常常遇到的问题就是项目快速迭代导致界面经常变更,脚本经常出错,控件的搜索与等待则可以缓解这一问题
//休眠指定时间void sleep(int time);//从当前页面搜索指定文本boolean searchText(String text);//等待控件/文本的出现boolean waitForView(int id);boolean waitForText(String text);//等待指定Activity的出现boolean waitForActivity(String name);//等待指定Log的出现boolean waitForLogMessage(String logMessage);//等待对话框的打开/关闭boolean waitForDialogToOpen();boolean waitForDialogToClose();//等待某种加载条件的达成boolean waitForCondition (Condition condition, int timeout);//等待Fragment的加载boolean waitForFragmentById (String id);boolean waitForFragmentByTag (String tag);
大多数API都是比较好理解的,这个Condition是一个接口,里面只提供了一个isSatisfied()方法,该方法返回boolean类型的值,需要我们自己写的,我们可以设置一些复杂的条件来实现这个接口
solo.waitForCondition(new Condition() { @Override public boolean isSatisfied() { return solo.isCheckBoxChecked(0); } },200);
而几乎所有的等待操作都可以设置超时时间,需要注意的是,Robotium中查找控件,点击控件等API都默认使用了搜索与等待机制,所以非必要的情况下一般是不需要我们主动设置等待的
5. 其他操作
剩下的一些操作包括截图,对软键盘的操作,点击物理按键等等
//截图,name为图片的参数,默认路径是/sdcard/Robotium-Screenshots/void takeScreenshot();void takeScreenshot(String name);//截取某段时间内一个序列void takeScreenshotSequence(String name);//关闭当前已打开的所有Activityvoid finishOpenedActivities();//点击返回键void goBack();//不断点击返回键直至返回到指定Activityvoid goBackToActivity(String name);// 收起键盘void hideSoftKeyboard();// 设置Activity转屏方向void setActivityOrientation(int orientation);
在自动化测试中,当执行失败是,除了Log,最方便解决定位问题的就是运行时的截图,Robotium中提供了棘突功能,
断言
在自动化测试中,我们获取控件,执行操作后,接下来就是要对操作后的场景进行断言了,断言就是我们在程序时的一些假设,就是我们断定在程序运行到某个时候,某个属性一定是多少,我们写程序的时候,大多数都是我们事先知道输入与输出的,断言就是来测试程序的输出与我们的输入是否相符
1.Junit中的断言
//断言传入的condition参数应该为True,否则抛出一个带有message提示的Throwable异常void assertTrue(String message, boolean condition);// 断言传入的condition 参数应该为False, 否则抛出message的异常void assertFalse(String message, boolean condition);//直接认为断言失败,抛出一个message异常void fail(String message);// 断言相等void assertEquals(String message,Obj o1, Obj o2);
Junit中的断言都在AndroidSdk中的 junit.framework.Assert包下的Assert类中, 常用的只有第一个,在使用的时候应该明确说明message参数的作用,方便我们后期查看
Button loginBtn = (Button) solo.getView("loginBtn");assertTrue("登录按钮应该显示", loginBtn.isShown());
2. Robotium中的断言
// 断言当前界面是否为name参数指定的Activity,若不是则抛出一个带有message提示的Throwable异常void assertCurrentActivity(String message, String name);//断言不处于低内存状态void assertMemoryNotLow();
Robotium基于Junit中的断言,也封装了几个方便在Android端自动化的时候使用,这些方法都是sole对象的方法
3.Android中的断言
//断言view在屏幕中void assertOnScreen(View origin, View view);//断言两个View是否低端对齐void assertBottomAligned(View first, View second);
这些断言都是Android提供的,可以方便的判断一些Android中的UI信息他们在android.test.ViewAsserts包下,但是目前(API level 24)这个类已经被标注成Deprecated了,目前Android提供了一个ViewMatchers的类来处理View的断言,而处理的思路和以前有了变化,核心方法是
static <T> void assertThat(String message T actual, Matcher<T> matcher);
几乎所有的判断用的都是这个方法,message 是抛出的异常信息,Matcher 是断言规则,之前的在屏幕上显示,底部对齐等都变成了一种断言规则,我们也可以写自己的规则,而T则是需要的View,这里使用的是泛型,也就是该断言框架可以不仅局限于用来断言View了 也可以用来断言任何东西,例如想要判读一个TextView是否显示某种文字
TextView textView = (TextView) solo.getView(R.id.main_tv);ViewMatchers.assertThat("显示: Welcome",textView,ViewMatchers.withText("Welcome"));
Robotium 原理
1.获取控件
我们来看一看solo对象通过view的id来获取控件的方法
public View getView(int id) { if(this.config.commandLogging) { Log.d(this.config.commandLoggingTag, "getView(" + id + ")"); } return this.getView(id, 0);}
Log先不用看,这个方法最后又调用了getView(int id, int index)的方法,那么我们再来看一看这个方法
public View getView(int id, int index) { if(this.config.commandLogging) { Log.d(this.config.commandLoggingTag, "getView(" + id + ", " + index + ")"); } View viewToReturn = this.getter.getView(id, index); if(viewToReturn == null) { String resourceName = ""; try { resourceName = this.instrumentation.getTargetContext().getResources().getResourceEntryName(id); } catch (Exception var6) { Log.d(this.config.commandLoggingTag, "unable to get resource entry name for (" + id + ")"); } int match = index + 1; if(match > 1) { Assert.fail(match + " Views with id: \'" + id + "\', resource name: \'" + resourceName + "\' are not found!"); } else { Assert.fail("View with id: \'" + id + "\', resource name: \'" + resourceName + "\' is not found!"); } } return viewToReturn;}
这段代码中重要的是View viewToReturn = this.getter.getView(id,index);这一句,其他的都是生成log,或者报错等,这个getter对象就是Robotium中专门用来获取View的类,那么再来看看这里面的方法
public View getView(int id, int index) { return this.getView(id, index, 0);}public View getView(int id, int index, int timeout) { return this.waiter.waitForView(id, index, timeout);}public View waitForView(int id, int index, int timeout) { if(timeout == 0) { timeout = Timeout.getSmallTimeout(); } return this.waitForView(id, index, timeout, false);}
在getView(int id, int index)方法里,又调用了 有timeout的方法重载,而在有timeout的方法重在里,又调用了waitForView,这样找到控件的方法,最后,就和等待的方法回合了,他们最后都调用了
waitForView(int id, int index, int timeout, boolean scroll)方法
一起来看一下这个方法
public View waitForView(int id, int index, int timeout, boolean scroll) { HashSet uniqueViewsMatchingId = new HashSet(); long endTime = SystemClock.uptimeMillis() + (long)timeout; while(SystemClock.uptimeMillis() <= endTime) { this.sleeper.sleep(); Iterator i$ = this.viewFetcher.getAllViews(false).iterator(); while(i$.hasNext()) { View view = (View)i$.next(); Integer idOfView = Integer.valueOf(view.getId()); if(idOfView.equals(Integer.valueOf(id))) { uniqueViewsMatchingId.add(view); if(uniqueViewsMatchingId.size() > index) { return view; } } } if(scroll) { this.scroller.scrollDown(); } } return null;}
这段代码就是最后来找到View的代码了,首先会创建出一个HashSet来存放View,接着会通过迭代器,从ViewFetcher中获取所有的View,这里传入的boolean值得意思是是否只获取可见的View,去除所有View之后,拿到他们的ID,再与我们传入的id进行对比,如果匹配成功就返回我们的View.那么拿到View的关键就是Robotium怎么获取的我们所有的View,接着一路看源码下去
static { try { String e; if(VERSION.SDK_INT >= 17) { e = "android.view.WindowManagerGlobal"; } else { e = "android.view.WindowManagerImpl"; } windowManager = Class.forName(e); } catch (ClassNotFoundException var1) { throw new RuntimeException(var1); } catch (SecurityException var2) { var2.printStackTrace(); }}public ArrayList<View> getAllViews(boolean onlySufficientlyVisible) { View[] views = this.getWindowDecorViews(); ArrayList allViews = new ArrayList(); View[] nonDecorViews = this.getNonDecorViews(views); View view = null; if(nonDecorViews != null) { for(int ignored = 0; ignored < nonDecorViews.length; ++ignored) { view = nonDecorViews[ignored]; try { this.addChildren(allViews, (ViewGroup)view, onlySufficientlyVisible); } catch (Exception var9) { ; } if(view != null) { allViews.add(view); } } } if(views != null && views.length > 0) { view = this.getRecentDecorView(views); try { this.addChildren(allViews, (ViewGroup)view, onlySufficientlyVisible); } catch (Exception var8) { ; } if(view != null) { allViews.add(view); } } return allViews;}public View[] getWindowDecorViews() { try { Field viewsField = windowManager.getDeclaredField("mViews"); Field instanceField = windowManager.getDeclaredField(this.windowManagerString); viewsField.setAccessible(true); instanceField.setAccessible(true); Object e = instanceField.get((Object)null); View[] result; if(VERSION.SDK_INT >= 19) { result = (View[])((ArrayList)viewsField.get(e)).toArray(new View[0]); } else { result = (View[])((View[])viewsField.get(e)); } return result; } catch (Exception var5) { var5.printStackTrace(); return null; }}
跟踪到最后发现,它通过反射拿到了一个叫WindowManagerGlobal的对象,并从这个对象中获得了顶级ViewDecorView,但是仔细观察它的反射方式,是直接通过类名构建了一个 新的WindowManagerGlobal对象 ,那么是如何从这个全新的WindowManagerGlobal中拿到DecorView的呢,只能从Android的源码入手分析了
在所有的Activity创建的过程中,我们知道都会有一个Window对象,当然真正的使用的是其子类PhoneWindow,PhoneWindow中会为Activity创建出一个DecorView对象,而DecorView是Activity的顶级View,在创建完成之后,还要通过WindowManager对象去将DecorView显示到屏幕上,这一步会调用WindowManager的makeVisible()方法
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE);}
在这一步中,会调用WindowManagerImpl的addView方法,WindowManagerImpl就是WindowManager真正的实现类,
@Overridepublic void addView(View view, ViewGroup.LayoutParams params{ mGlobal.addView(view, params, mDisplay, mParentWindow);}
会将DecorView添加到一个叫WindowManagerGlobal的对象中,这个对象中有一个叫sDefaultWindowManager的静态的WindowManagerGlobal,所有的DecorView都会添加到这个sDefaultWindowManager的一个成员变量mViews中,它是一个专门用来存放所有的DecorView的ArrayList,同时又因为sDefaultWindowManager是静态的,所以在全局无论通过什么途径拿到它,拿到的都是同一个对象
2. 控件的操作
Robotium获取控件后,调用clickOnView(View view)方法就可以完成点击操作,这个方法可以分为两步实现
1. 根据View获取控件在屏幕中的位置
2. 根据坐标模拟点击事件
对WebView的支持
Robotium 在 4.0 开始 全面支持WebView,它支持通过ID,className等方式来获取WebElement元素
//获取当前WebView所有的WebElement元素ArrayList<WebElement> getCurrentWebElements();//通过By根据指定的元素获取当前WebView的所有WebElement元素ArrayList<WebElement> getCurrentWebElements(By by);//通过by根据指定的元素属性点击WebElementvoid clickOnWebElement(By by);//点击指定的WebElementvoid clickOnWebElement(WebElement webElement);//根据by找到WebElement,并输入指定的文本textvoid enterTextInWebElement(By by, String text);//等待根据by获得的WebElement出现boolean waitForWebElement(By by);
By的其实就是当我们知道了Web元素的某种属性之后来通过该属性来获取控件的,例如
ArrayList<WebElement> webElements = solo.getCurrentWebElements();ArrayList<WebElement> webElements = solo.getCurrentWebElements(By.id("example_id"));
在使用Robotium操作WebView的时候需要注意的是,它只支持原生的WebView而不支持第三方浏览器内核
- Android平台Robotium UI测试详解
- UI自动化测试Robotium
- Android Robotium自动化测试
- android 自动化测试robotium
- ANDROID自动化测试 robotium
- Android Robotium自动化测试
- Robotium android自动化测试
- Android Robotium测试框架
- robotium android 自动化UI测试获取控件是否隐藏的属性
- Android自动化测试之robotium
- Android Robotium测试APK方法
- Android Robotium自动化测试二
- 转:ANDROID自动化测试 robotium
- ANDROID自动化测试工具:ROBOTIUM
- Android Robotium自动化测试二
- Android 自动化测试—robotium
- android 使用Robotium自动化测试
- Jenkins+Ant+Android+Robitium 实例详解(打包app,执行Robotium测试,生成测试结果)
- 警惕一大波银行类木马正在靠近,新型BankBot木马解析
- C# 服务端
- 数据结构 day1
- 3D转换 导航栏
- hdu 1812 Count the Tetris (置换)
- Android平台Robotium UI测试详解
- 如何上传自己的代码到Git上
- [Aha]镖局运镖
- <32>python学习笔记——常用模块
- HBase 1.0.0 API的变化
- 258. Add Digits
- 指针例题详解
- 聚合maven+spring-boot打包可执行jar
- Ubuntu下QT creator查看pixhawk工程