Robotium框架的使用和源码解析
来源:互联网 发布:医药类软件 编辑:程序博客网 时间:2024/06/15 19:47
使用:
1、添加依赖
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.3'
2、添加测试代码,例如:
@RunWith(AndroidJUnit4.class)public class MainActivityTest { @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class); private Solo solo; @Before public void setUp() throws Exception{ // 获取activity solo = new Solo(InstrumentationRegistry.getInstrumentation(),activityTestRule.getActivity()); } @After public void tearDown() throws Exception { solo.finishOpenedActivities(); // 关闭打开的活动 } @Test public void testAddNote() throws Exception { solo.clickOnView(solo.getView(R.id.test)); // 点击view }}
robotium框架是通过模拟用户操作手机屏幕来进行测试,几个基本操作:
- 要知道操作控件的坐标
- 对控件进行模拟操作
- 判断操作的结果是否符合预期
控件的获取
1、通过id获取
solo.getView(R.id.test);
2、通过索引、文本获取
solo.getText(0); // 第一个Textview控件solo.getText("hello"); // 包含"hello"文字的textView控件(文字部分匹配即可)
3、通过对控件类型进行过滤获取
// 比如:一个LinearLayout中子控件都是动态添加的,就可以用这种方式获取LinearLayout parentView = (LinearLayout) solo.getView(R.id.test);ArrayList<TextView> textViews = solo.getCurrentViews(TextView.class, parentView);textViews.get(0);// getCurrentViews()方法有4个重载方法,可以指定过滤条件
控件的操作
1、点击、长按操作
clickOnView(View view) / clickLongOnView(View view)clickOnScreen(float x,float y) / clickLongOnScreen(float x,float y)
2、输入框操作
enterText(EditText editText,String text) // 直接对文本框赋值typeText(EditText editText,String text) // 一个一个文本的输入clearText(EditText editText)
3、滑动、滚动
// 从起始坐标滑动到终点坐标,通过setCount参数指定滑动的步长drag(float fromX, float toX, float fromY, float toY, int setpCount) // 滑动至屏幕的顶部/滑动至屏幕的顶部底部scrollToTop()/scrollToDown()// 向上滑一屏/向下滑一屏(不能触发ListView的加载更多的监听)scrollUp()/scrollDown()
4、搜索和等待
sleep(int time) // 休眠boolean searchText(String text) // 从当前页面搜索指定文本boolean waitForView(int id)/waitForText(String text) // 等待指定控件/文本出现boolean waitForActivity(String name) // 等待指定的Activity出现boolean waitForLogMessage(String logMessage) // 等待指定的日志信息出现boolean waitForDialogToOpen()/waitForDialogToClose() // 等待指定弹窗打开/关闭
Robotium中查找控件、点击控件等api都默认使用了搜索等待机制,因此非特殊情况是不用添加额外的等待操作。
5、截图及其他
takeScreenshot(String name) // 截图,图片名为name,默认路径为/sdcard/Robotium-ScreenshotsfinishOpenedActivities() // 关闭已经打开的所有activitygoBack() / goBackToActivity(String name) // 点击返回键/不断的点击返回键直至返回到指定的activityhideSoftKeyboard() // 收起键盘setActivityOrientation(int orientation) // 设置Activity转屏方向
其他:Robotium 4.0开始支持对webView的进行测试,提供了相关的api,步骤和native测试一样,获取元素、操作、断言。
断言
1、Junit中的断言
// condition应该为true。当condition为false时,抛出Message异常void assertTrue(String message, boolean condition)// condition应该为false。当condition为true时,抛出Message异常void assertFalse(String message, boolean condition)// 直接使用例失败,并抛出message异常void fail(String message)
2、Robotium中的断言
// 当前页面是否为name参数指定的activityvoid assertCurrentActivity(String message, String name) // 当前是否处于低内存状态void assertMemoryNowLow()
3、android中断言的api
// 断言view是否在屏幕中assertOnSreen(View origin, View view)// 断言两个View是否底端对齐,即它们的底端y坐标是否相等assertBottomAligned(View first, View second)...
源码解析
getView获取流程:
public View getView(int id) { return this.getView(id, 0); }
public View getView(int id, int index) { View viewToReturn = this.getter.getView(id, index); // 这个方法下面会分析 if(viewToReturn == null) { // 没有获取到view,断言失败 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; }
下面看this.getter.getView(id, index)方法
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(); // 初始化的时候会设置超时时间为10s } return this.waitForView(id, index, timeout, false); }
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(); // Thread.sleep(500); Iterator i$ = this.viewFetcher.getAllViews(false).iterator(); // 通过反射获取到当前页面所有的view 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(); } } // 在endTime时间内还没获取到控件,则返回null return null; }
总结一下就是,通过反射获取当前页面的view,在10s内不断循环,和当前的getView传入的id配置是否相同,如果相同就返回view。
下面看一下clickOnView
public void clickOnView(View view) { view = this.waiter.waitForView(view, Timeout.getSmallTimeout()); this.clicker.clickOnScreen(view); }
先看waitForView()方法
public View waitForView(View view, int timeout) { return this.waitForView(view, timeout, true, true); }
public View waitForView(View view, int timeout, boolean scroll, boolean checkIsShown) { long endTime = SystemClock.uptimeMillis() + (long)timeout; int retry = 0; if(view == null) { return null; } else { label39: do { for(; SystemClock.uptimeMillis() < endTime; this.sleeper.sleep()) { boolean foundAnyMatchingView = this.searcher.searchFor(view); if(checkIsShown && foundAnyMatchingView && !view.isShown()) { // 如果找到这个view,这个view没有显示,则sleep 300ms this.sleeper.sleepMini(); ++retry; View identicalView = this.viewFetcher.getIdenticalView(view); if(identicalView != null && !view.equals(identicalView)) { view = identicalView; } continue label39; } if(foundAnyMatchingView) { // 如果找到匹配的view则返回 return view; } if(scroll) { this.scroller.scrollDown(); } } return view; } while(retry <= 5); return view; } }
下面看clickOnScreen()方法
public void clickOnScreen(View view) { this.clickOnScreen(view, false, 0); }
public void clickOnScreen(View view, boolean longClick, int time) { if(view == null) { Assert.fail("View is null and can therefore not be clicked!"); } float[] xyToClick = this.getClickCoordinates(view); // 获取点击的坐标 float x = xyToClick[0]; float y = xyToClick[1]; if(x == 0.0F || y == 0.0F) { this.sleeper.sleepMini(); try { view = this.viewFetcher.getIdenticalView(view); // 获取找到的view } catch (Exception var8) { ; } if(view != null) { xyToClick = this.getClickCoordinates(view); x = xyToClick[0]; y = xyToClick[1]; } } if(longClick) { this.clickLongOnScreen(x, y, time, view); } else { this.clickOnScreen(x, y, view); // 点击view } }
总结一下就是获取view,然后获取view的坐标,触发点击事件。
阅读全文
0 0
- Robotium框架的使用和源码解析
- robotium测试框架的调研使用
- 自动化测试框架Robotium简单使用和分析
- 使用retrofit2和rxjava封装的网络框架RNet:(二)RNet的源码解析
- 使用robotium无源码APK测试碰到的问题
- Robotium源码webview的实现
- Robotium源码webview的实现
- EventBus的使用和源码解析
- Picasso的使用和源码解析
- Okio的使用和源码解析
- Picasso的使用和源码解析
- EventBus的使用和源码解析
- Retrofit的使用和源码解析
- Robotium的id使用
- robotium的使用
- Robotium的使用技巧
- robotium框架的学习记录
- Eclipse使用Maven导入Robotium源码后报错
- 音频与视屏API
- git(九)-git refspec以及git别名
- 数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示
- 算法常用的时间复杂度(log(n),sqrt(x),n,n*log(n),n^2,n^3,2^n)的函数坐标图
- gcd区间 洛谷p1890
- Robotium框架的使用和源码解析
- yii数据库操作
- MyISAM和InnoDB索引
- 8月1日小结
- github常见操作和常见错误!错误提示:fatal: remote origin already exists.
- KEIL认识中1--生成多个工程
- canny算子—快速边缘检测
- XPATH的基本用法
- usb hub模块调试