说明

  在项目配置完基于robotium框架下的自动化测试用例后发现虽然用代码配置测试用例虽然较为灵活,但是如果编写较为全面的测试用例则必然会消耗大量开人员的精力,并且对于用例的后期维护也是很大一部分投入,使开发人员无法更为专注于项目构建,如此萌生了将测试用例以HTML脚本方式进行表述,可让测试人员进行配置测试脚本,测试人员在进行全流程业务测试过程中进行自动化测试脚本的编写,并且可进行增量维护,在项目上线前可以用最小的时间代价进行最全面的测试工作,对于项目质量的把控有不可忽视的作用。

  本自动化测试项目需要开发人员针对项目进行一定的配置,当配置完成后可对自动化测试项目进行打包,与被测项目安装在同一设备内,通过adb执行指定命令完成自动化测试的流程。除非有重大变,自动化测试项目仅需开发一次,可针对同一被测项目重复使用,所有测试流程的设备行为由HTML标签进行约束,测试人员以约定HTML标签编写测试用例,可动态指定执行的测试用例,便于测试用例的维护。

执行环境:

1、被测试项目安装包

2、自动化测试项目安装包

3、Android开发环境(示例开发工具为eclipse)

4、Android设备

自动化测试项目初始构建

1、新建被测试项目,并编写相关功能。

2、创建测试项目,关联被测试项目。

3、测试项目导入robotium-solo jar包(可于官网下载)

4、编写代码自动化测试用例,测试robotium自动化测试是否可正常执行

开发工具中执行测试用例:光标放在当前类中,点击鼠标右键 --> RunAs --> Android Junit Test


  1. 1、创建一个Android项目,编辑项目清单文件
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  4. package="com.walker.autotest"
  5. android:versionCode="1"
  6. android:versionName="1.0" >
  7. <uses-sdk android:minSdkVersion="9" />
  8. <!-- 必须指定,并设置目标项目包名:com.waitingfy.iosunlock 替换成目标项目的包名-->
  9. <instrumentation
  10. android:name="android.test.InstrumentationTestRunner"
  11. android:targetPackage="com.waitingfy.iosunlock" />
  12. <application
  13. android:icon="@drawable/ic_launcher"
  14. android:label="@string/app_name" >
  15. <!-- 必须指定所用library(拷贝即可) -->
  16. <uses-library android:name="android.test.runner" />
  17. </application>
  18. </manifest>

  1. 2 增加自动化测试用例执行入口,代码如下:
  2. /**
  3. * @Title: HtmlScariptTest.java
  4. * @Package com.walker.autotest
  5. * @Description: TODO
  6. * @author A18ccms A18ccms_gmail_com
  7. * @date 2017年9月4日 下午2:00:09
  8. * @version V1.0
  9. */
  10. package com.walker.autotest;
  11. import com.robotium.solo.Solo;
  12. import android.os.Environment;
  13. import android.test.ActivityInstrumentationTestCase2;
  14. import android.util.Xml;
  15. /** @ClassName: HtmlScariptTest
  16. *
  17. * @Description: TODO
  18. *
  19. * @author walker
  20. *
  21. * @date 2017年9月4日 下午2:00:09
  22. *
  23. */
  24. public class HtmlScariptTest extends ActivityInstrumentationTestCase2{
  25. Solo solo;
  26. String logFileName = "testLog.txt";
  27. private static Class<?> launchActivityClass;
  28. //被测试应用入口界面Activity全名
  29. private static String mainActiviy = "com.waitingfy.iosunlock.MainActivity";
  30. //被测试应用包名
  31. private String packageName = "com.waitingfy.iosunlock";
  32. //加载入口Activity,获取Activity的类对象
  33. static {
  34. try {
  35. launchActivityClass = Class.forName(mainActiviy);
  36. } catch (ClassNotFoundException e) {
  37. throw new RuntimeException(e);
  38. }
  39. }
  40. /**
  41. * 默认构造函数,以入口Activity对象为参数调用父类构造,设定测试用例入口执行逻辑
  42. */
  43. public HtmlScariptTest() {
  44. super(launchActivityClass);
  45. }
  46. /**
  47. * 测试用例初始化时设置solo对象,对设备的所有操作通过solo对象进行。
  48. */
  49. @Override
  50. protected void setUp() throws Exception {
  51. super.setUp();
  52. solo = new Solo(getInstrumentation(),getActivity());
  53. }
  54. //必须覆写
  55. @Override
  56. protected void tearDown() throws Exception {
  57. super.tearDown();
  58. }
  59. /**
  60. * Add by walker Date 2017年9月4日
  61. * @Description: TODO
  62. * 测试用例执行入口,测试用例方法名以test开头命名
  63. */
  64. public void test0(){
  65. solo.sleep(10000);
  66. }

标签设计

标签 动作 属性
testCase 指定测试用例 测试用例文件名
data 标记内容为测试数据 dataType 各标签值
clickView 点击指定ID的View控件 ID,hasText
inputText 指定编辑框录入内容 ID 输入框录入内容
checkBox 设置指定选择框的勾选属性 ID 值为1勾选,否则不勾选
spinner 列表选择 ID 被选择内容的文本值
sleep 延迟时间后再继续执行 延时时长(毫秒)
onKeyDown 键盘按键事件 按键值,支持范围如下
clickOnText 点击显示的文本 hasText 被点击的文本字符串
takeScreenshot 截屏并保存图片
waitForActivity 等待指定页面 指定页面的activity值
checkData 校验数据标签 tableName
filed 数据校验字段 filedName 指定字段的值

testCase:仅在EMS.xml中起作用,且Main.xml文件中只识别testCase标签

data:包裹测试数据,被包裹内容为测试脚本数据,属性dataType来设定数据类型。

clickView:点击页面View控件,属性有ID、hasText。ID属性不可为空,以ID值进行索引View控件并进行点击;hasText属性值可以为空,如果hasText为空或者在当前可见页面可以所搜到此文本则执行点击事件。

clickOnText:点击页面上指定的文本,属性有hasText。如果hasText为空或者在当前可见页面可以搜索到此文本则执行点击事件。

waitForActivity:值为页面Activity名称,等待指定的Activity,如果不是指定activity则结束测试流程

spinner:点击控件后显示候选列表,并从列表中选择标签指定的值。

takeScreenshot:截图文件保存路径(/sdcard/Robotium-Screenshots)

checkData:用于指定校验数据,属性tableName为被查询的表名,标签包裹内容为字段属性值

filed:用于指定被校验表的字段属性,属性filedName指定字段名称,值为字段的值。此标签应被checkData标签包裹。

  1. 支持的按键:back(回退键)、enter(确认键)、left(左键)、right(右方向键)、up(上方向键)、down(下方向键)、数字(1-9);
  2. ID属性:为项目中xml文件中配置id值,可通过项目源码获取

代码解析HTML标签配置的测试用例

  如下代码为带有HTML标签解析的代码,在执行测试用例时首先对指定路径下的配置文件main.xml进行读取,根据其配置的脚本文件加载对应的执行脚本,然后依次执行所配置的脚本,完成自动化测试流程。


  1. /**
  2. * @Title: HtmlScariptTest.java
  3. * @Package com.walker.autotest
  4. * @Description: TODO
  5. * @author A18ccms A18ccms_gmail_com
  6. * @date 2017年9月4日 下午2:00:09
  7. * @version V1.0
  8. */
  9. package com.walker.autotest;
  10. import java.io.File;
  11. import java.io.FileInputStream;
  12. import java.io.InputStream;
  13. import java.util.ArrayList;
  14. import java.util.HashMap;
  15. import java.util.Iterator;
  16. import java.util.Map;
  17. import java.util.Map.Entry;
  18. import java.util.Set;
  19. import org.xmlpull.v1.XmlPullParser;
  20. import com.robotium.solo.Solo;
  21. import android.os.Environment;
  22. import android.test.ActivityInstrumentationTestCase2;
  23. import android.util.Xml;
  24. import android.view.KeyEvent;
  25. import android.view.View;
  26. import android.widget.CheckBox;
  27. import android.widget.EditText;
  28. /**
  29. * @ClassName: HtmlScariptTest
  30. *
  31. * @Description: TODO
  32. *
  33. * @author walker
  34. *
  35. * @date 2017年9月4日
  36. *
  37. */
  38. public class HtmlScariptTest extends ActivityInstrumentationTestCase2 {
  39. Solo solo;
  40. String logFileName = "testLog.txt";
  41. private static Class<?> launchActivityClass;
  42. // 被测试应用入口界面Activity全名
  43. private static String mainActiviy = "com.waitingfy.iosunlock.MainActivity";
  44. // 被测试应用包名
  45. private String packageName = "com.waitingfy.iosunlock";
  46. // 加载入口Activity,获取Activity的类对象
  47. static {
  48. try {
  49. launchActivityClass = Class.forName(mainActiviy);
  50. } catch (ClassNotFoundException e) {
  51. throw new RuntimeException(e);
  52. }
  53. }
  54. /**
  55. * 默认构造函数,以入口Activity对象为参数调用父类构造,设定测试用例入口执行逻辑
  56. */
  57. public HtmlScariptTest() {
  58. super(launchActivityClass);
  59. }
  60. /**
  61. * 测试用例初始化时设置solo对象,对设备的所有操作通过solo对象进行。
  62. */
  63. @Override
  64. protected void setUp() throws Exception {
  65. super.setUp();
  66. solo = new Solo(getInstrumentation(), getActivity());
  67. }
  68. @Override
  69. protected void tearDown() throws Exception {
  70. super.tearDown();
  71. }
  72. /**
  73. * Add by walker Date 2017年9月4日
  74. *
  75. * @Description: TODO 测试用例执行入口,测试用例方法名以test开头命名
  76. */
  77. public void test0() {
  78. loadScriptList();
  79. }
  80. String configPath = "";
  81. /**
  82. * Add by walker Date 2017年8月29日
  83. *
  84. * @Description: TODO 加载解析脚本清单配置文件配置脚本文件
  85. */
  86. private void loadScriptList() {
  87. configPath = Environment.getExternalStorageDirectory() + "/Test/";
  88. File file = new File(configPath + "Main.xml");
  89. try {
  90. InputStream in = new FileInputStream(file);
  91. XmlPullParser parser = Xml.newPullParser();
  92. parser.setInput(in, "utf-8");
  93. int type = parser.getEventType();
  94. while (type != XmlPullParser.END_DOCUMENT) {
  95. String value = "";
  96. switch (type) {
  97. case XmlPullParser.START_TAG:
  98. value = parser.nextText();
  99. if ("testCase".equals(parser.getName())) {
  100. // 读取脚本文件,加载执行所配置的脚本文件
  101. xmlLoad(value);
  102. }
  103. break;
  104. case XmlPullParser.END_TAG:
  105. break;
  106. }
  107. type = parser.next();
  108. }
  109. } catch (Exception e) {
  110. e.printStackTrace();
  111. }
  112. }
  113. /** 数据标志:1-data类型; 2-checkData */
  114. int dataFlag = 0;
  115. HashMap<String, String> dataMap = new HashMap<String, String>();
  116. HashMap<String, String> lableAction = new HashMap<String, String>();
  117. /**
  118. * Add by walker Date 2017年8月29日
  119. *
  120. * @Description: TODO 加载自动化测试脚本,并执行自动化测试
  121. * @param fileName
  122. * 自动化测试脚本名称
  123. */
  124. public void xmlLoad(String fileName) {
  125. File file = new File(configPath + fileName);
  126. try {
  127. InputStream in = new FileInputStream(file);
  128. // 创建xmlPull解析器
  129. XmlPullParser parser = Xml.newPullParser();
  130. /// 初始化xmlPull解析器
  131. parser.setInput(in, "utf-8");
  132. // 读取文件的类型
  133. int type = parser.getEventType();
  134. int depth = parser.getDepth();
  135. String id = "";
  136. String value = "";
  137. String hasText = "";
  138. String dataType = "";
  139. String filedName = "";
  140. String tableName = "";
  141. ArrayList<Map<String, String>> actionList = new ArrayList<Map<String, String>>();
  142. while ((type != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
  143. id = "";
  144. value = "";
  145. hasText = "";
  146. switch (type) {
  147. // 开始标签
  148. case XmlPullParser.START_TAG:
  149. id = parser.getAttributeValue(null, "id");
  150. hasText = parser.getAttributeValue(null, "hasText");
  151. filedName = parser.getAttributeValue(null, "filedName");
  152. if ("data".equals(parser.getName())) {
  153. actionList = new ArrayList<Map<String, String>>();
  154. dataFlag = 1;
  155. dataType = parser.getAttributeValue(null, "dataType");
  156. break;
  157. }
  158. if ("checkData".equals(parser.getName())) {
  159. actionList = new ArrayList<Map<String, String>>();
  160. dataMap = new HashMap<String, String>();
  161. tableName = parser.getAttributeValue("", "tableName");
  162. dataMap.put("dataName", tableName);
  163. dataFlag = 2;
  164. break;
  165. }
  166. value = parser.nextText();
  167. lableAction = new HashMap<String, String>();
  168. lableAction.put("dataType", dataType);
  169. lableAction.put("ID", id);
  170. lableAction.put("hasText", hasText);
  171. lableAction.put("tableName", tableName);
  172. lableAction.put("filedName", filedName);
  173. lableAction.put("value", value);
  174. lableAction.put("actionName", parser.getName());
  175. actionList.add(lableAction);
  176. if (dataFlag == 2 && !isEmptyUnNull(filedName) && !dataMap.containsKey(filedName)) {
  177. dataMap.put(filedName, value);
  178. } else {
  179. }
  180. if ("clickView".equals(parser.getName())) {// 按钮
  181. if (isEmptyUnNull(hasText) || solo.searchText(hasText, true)) {
  182. clickOnView(id);
  183. }
  184. } else if ("inputText".equals(parser.getName())) {// 文本输入框
  185. enterText(id, value);
  186. } else if ("checkBox".equals(parser.getName())) {// 复选框
  187. if ("1".equals(value)) {
  188. chkSelect(id, true);
  189. } else {
  190. chkSelect(id, false);
  191. }
  192. } else if ("spinner".equals(parser.getName())) {// 下拉选择
  193. selectData(value, id);
  194. } else if ("sleep".equals(parser.getName())) {// 睡眠时间
  195. try {
  196. sleep(Integer.parseInt(value) / 1000);
  197. } catch (Exception e) {
  198. e.printStackTrace();
  199. }
  200. } else if ("onKeyDown".equals(parser.getName())) {// 按键事件
  201. sendKey(value);
  202. } else if ("clickOnText".equals(parser.getName())) {// 点击文本
  203. solo.clickOnText(value + "");
  204. } else if ("takeScreenshot".equals(parser.getName())) {// 屏幕拍照
  205. solo.takeScreenshot();
  206. solo.sleep(300);
  207. } else if ("waitForActivity".equals(parser.getName())) {
  208. sleep(2);
  209. if (solo.waitForActivity(value)) {
  210. break;
  211. } else {
  212. return;
  213. }
  214. }
  215. break;
  216. case XmlPullParser.END_TAG:
  217. if ("data".equals(parser.getName())) {
  218. dataType = "";
  219. } else if ("checkData".equals(parser.getName())) {
  220. sleep(1);
  221. checkData();
  222. }
  223. break;
  224. }
  225. type = parser.next();
  226. }
  227. } catch (Exception e) {
  228. e.printStackTrace();
  229. }
  230. }
  231. /**
  232. * Add by walker Date 2017年9月1日
  233. *
  234. * @Description: TODO 根据配置文件校验数据
  235. */
  236. private void checkData() {
  237. StringBuilder sql = new StringBuilder();
  238. sql.append("select * from ");
  239. if (dataMap != null && dataMap.size() > 1 && !isEmptyUnNull(dataMap.get("dataName"))) {
  240. sql.append(dataMap.get("dataName") + " where ");
  241. Set<Entry<String, String>> set = dataMap.entrySet();
  242. Iterator<Entry<String, String>> it = set.iterator();
  243. Map.Entry<String, String> me;
  244. while (it.hasNext()) {
  245. me = it.next();
  246. if ("dataName".equals(me.getKey())) {
  247. } else {
  248. sql.append(me.getKey() + "='" + me.getValue() + "' and ");
  249. }
  250. }
  251. if ((sql + "").endsWith("and ")) {
  252. sql.delete(sql.lastIndexOf("and"), sql.length());
  253. }
  254. // 校验数据逻辑,可根据具体项目进行配置,执行sql查询语句,获取查询结果,如在指定条件下获取查询结果为空,那么
  255. HashMap<String, String> data = null;
  256. // data = DbUtils.getInstance().queryFirstData(sql +"");
  257. if (data != null && data.size() > 0) {
  258. } else {
  259. }
  260. } else {
  261. return;
  262. }
  263. }
  264. /**
  265. * Add by walker Date 2017年9月4日
  266. *
  267. * @Description: TODO 延时器
  268. * @param time
  269. * 延时时间,单位为秒
  270. */
  271. private void sleep(int time) {
  272. long start = System.currentTimeMillis();
  273. while ((System.currentTimeMillis() - start) < time * 1000) {
  274. }
  275. }
  276. /**
  277. * Add by walker Date 2017年9月4日
  278. *
  279. * @Description: TODO 判断字符串是否为空
  280. * @param value
  281. * 字符串的值
  282. * @return 如果字符串为null或""或"null"或者全部为空白字符则返回true,否则返回false。
  283. */
  284. public static boolean isEmptyUnNull(String value) {
  285. if (value != null && !"".equalsIgnoreCase(value.trim()) && !"null".equalsIgnoreCase(value.trim())
  286. && value.trim().length() != 0) {
  287. return false;
  288. } else {
  289. return true;
  290. }
  291. }
  292. /**
  293. * Add by walker Date 2017年9月4日
  294. *
  295. * @Description: TODO 模拟按键事件,按键前后进行延时
  296. * @param keyStr
  297. * 按键字符
  298. * 支持的按键:back(回退键)、enter(确认键)、left(左键)、right(右方向键)、up(上方向键)、down(
  299. * 下方向键)、数字(1-9)
  300. */
  301. public void sendKey(String keyStr) {
  302. int key = 0;
  303. if ("1".equals(keyStr)) {
  304. key = KeyEvent.KEYCODE_1;
  305. } else if ("3".equals(keyStr)) {
  306. key = KeyEvent.KEYCODE_3;
  307. } else if ("4".equals(keyStr)) {
  308. key = KeyEvent.KEYCODE_4;
  309. } else if ("5".equals(keyStr)) {
  310. key = KeyEvent.KEYCODE_5;
  311. } else if ("6".equals(keyStr)) {
  312. key = KeyEvent.KEYCODE_6;
  313. } else if ("7".equals(keyStr)) {
  314. key = KeyEvent.KEYCODE_7;
  315. } else if ("8".equals(keyStr)) {
  316. key = KeyEvent.KEYCODE_8;
  317. } else if ("9".equals(keyStr)) {
  318. key = KeyEvent.KEYCODE_9;
  319. } else if ("back".equals(keyStr)) {
  320. key = KeyEvent.KEYCODE_BACK;
  321. } else if ("enter".equals(keyStr)) {
  322. key = KeyEvent.KEYCODE_DPAD_CENTER;
  323. } else if ("left".equals(keyStr)) {
  324. key = KeyEvent.KEYCODE_DPAD_LEFT;
  325. } else if ("right".equals(keyStr)) {
  326. key = KeyEvent.KEYCODE_DPAD_RIGHT;
  327. } else if ("up".equals(keyStr)) {
  328. key = KeyEvent.KEYCODE_DPAD_UP;
  329. } else if ("down".equals(keyStr)) {
  330. key = KeyEvent.KEYCODE_DPAD_DOWN;
  331. }
  332. solo.sleep(30);
  333. solo.sendKey(key);
  334. solo.sleep(30);
  335. }
  336. /**
  337. * Add by walker Date 2017年9月4日
  338. *
  339. * @Description: TODO 选择对应信息
  340. * @param selectedStr
  341. * 选择的类型(文本内容)
  342. * @param id
  343. * 下拉选择控件ID
  344. * @param title
  345. * 选择框的标题字符串
  346. * @param ranges
  347. * 被选择范围-所选择的字符串必须在此范围内
  348. */
  349. public void selectData(String selectedStr, String id) {
  350. clickOnView(id);
  351. solo.sleep(1000);
  352. solo.clickOnText(selectedStr);
  353. }
  354. /**
  355. * Add by walker Date 2017年9月4日
  356. *
  357. * @Description: TODO 点击ID所指定的控件
  358. * @param viewId
  359. * 视图控件ID
  360. */
  361. public void clickOnView(String viewId) {
  362. try {
  363. View view = getView(solo, viewId);
  364. solo.clickOnView(view);
  365. } catch (Exception e) {
  366. e.printStackTrace();
  367. }
  368. }
  369. public View getView(Solo solo, String idStr) {
  370. int id = solo.getCurrentActivity().getResources().getIdentifier(idStr, "id", packageName);
  371. View v = solo.getView(id);
  372. return v;
  373. }
  374. /**
  375. * Add by walker Date 2017年9月4日
  376. *
  377. * @Description: TODO 勾选框勾选功能
  378. * @param id
  379. * checkBox控件ID
  380. * @param checked
  381. * 设置是否选择此控件
  382. */
  383. public void chkSelect(String id, final boolean checked) {
  384. CheckBox chk = (CheckBox) solo.getView(id);
  385. if (!chk.isChecked() && checked) {
  386. solo.clickOnView(chk);
  387. }
  388. }
  389. /**
  390. * Add by walker Date 2017年9月4日
  391. *
  392. * @Description: TODO 为文本录入框录入字符串,文本录入完成后延迟指定时间
  393. * @param id
  394. * 文本录入框ID
  395. * @param msg
  396. * 文本录入信息
  397. */
  398. public void enterText(String id, String msg) {
  399. EditText editText = (EditText) getView(solo, id);
  400. if (editText != null) {
  401. solo.clickOnView(editText);
  402. solo.clearEditText(editText);
  403. solo.enterText(editText, msg + "");
  404. }
  405. solo.sleep(200);
  406. }
  407. }

脚本编写

1、新建main.xml配置文件,根据标签testCase指定测试脚本文件。

2、根据动作标签编写测试脚本用例,以main.xml中指定的文件名为名。

3、将main.xml文件及脚本配置文件放到Android设备sd卡下Test文件夹下(如果文件夹不存在,那么新建此文件夹,文件名区分大小写)

执行脚本

  在目标Android设备安装上测试apk及被测试apk签名后在电脑命令行上执行如下命令即可实现自动化测试启动流程。


  1. adb shell am instrument -e class com.walker.autotest.HtmlScariptTest -w com.walker.autotest/android.test.InstrumentationTestRunner

注意事件

1、测试apk安装包与被测试项目安装包apk的应用签名要一致。

2、测试项目如果引用库工程或者V4包时需要注意有无重复的包,本用例编写时测试工程引用了V4包,导致被测应用启动失败。

HTML脚本配置Android自动化测试的更多相关文章

  1. 解放双手——Android自动化测试

    解放程序猿宝贵的右手(或者是左手) http://blog.csdn.net/eclipsexys/article/details/45622813 --Android自动化测试技巧 Google大神 ...

  2. Android 自动化测试框架

    Android常用的自动化测试工具框架: Monkey,MonkeyRunner,UIAutomator,Robotium,Appium,Monkey Talk...... 但这些工具框架都是什么呢有 ...

  3. MonkeyRunner原理初步--Android自动化测试学习历程

    章节:自动化基础篇——MonkeyRunner原理初步 主要讲解内容及笔记: 一.理论知识和脚本演示 最佳方式是上官网文档去查看monkeyrunner的介绍,官网上不去,就找了一个本地的androi ...

  4. 【Mac + Appium + Python3.6学习(五)】之常用的Android自动化测试API总结

    Github测试样例地址:https://github.com/appium-boneyard/sample-code/tree/master/sample-code/examples ①定位text ...

  5. [技术博客] Android 自动化测试

    [技术博客] Android 自动化测试 安卓自动化测试工具与平台的搭建 类似于网页端自动化,安卓测试的自动化也主要是针对控件的自动化.其原理就是通过python(其他语言) 的脚本来代替我们手动完成 ...

  6. Android自动化测试--monkey总结

    什么是 Monkey Monkey 是一个 Android 自动化测试小工具.主要用于Android 的压力测试, 主要目的就是为了测试app 是否会Crash. Monkey 特点 顾名思义,Mon ...

  7. 使用 flow.ci 实现 Android 自动化测试与持续集成

    在上篇文章--如何实现 Android 应用的持续部署中,我们使用的是 flow.ci + Github + fir.im 实现 Android 应用的持续部署.对于 Android 开发者,他们可能 ...

  8. Android自动化测试-Robotium(一)简介

    一.Robotium原理 Robotium是一款Android自动化测试框架,主要针对Android平台的应用进行黑盒自动化测试,它提供了模拟各种手势操作(点击.长按.滑动等).查找和断言机制的API ...

  9. 转:Android开发实践:用脚本编译Android工程

    转自: http://ticktick.blog.51cto.com/823160/1365947 一般情况下,我们都是使用Eclipse+ADT插件或者Android studio软件来编译Andr ...

随机推荐

  1. 将Asp.Net Core和corefx移植到.Net 4.0

    引言 因为工作内容的原因需要兼容 XP,而 XP 最多支持到.Net Framework 4.0.因此无法享受到 .Net Core 带来的一堆很好用的库,好在无论 corefx 还是 Asp.Net ...

  2. Let's Encrypt与DNS轮循

    本文由网络安全研究员.securityheaders.io和report-uri.io创始人Scott Helme发布在其个人博客中.描述了如何使用Let's Encrypt的同时兼容DNS轮循. 早 ...

  3. Android 性能测试——Heap Viewer 工具

    Android 性能测试--Heap Viewer 工具 Heap Viewer能做什么? 实时查看App分配的内存大小和空闲内存大小 发现Memory Leaks Heap Viewer使用条件 5 ...

  4. 表达式求值(栈方法/C++语言描述)(三)

    代码清单 // calculator.h #ifndef CALCULATOR_H #define CALCULATOR_H #include <stack> #include <s ...

  5. 尝试在CentOS7.2上编译安装Swift

    苹果提供 Ubuntu上构建Swift 的教程,通过这个教程我尝试使用CentOS7.2上玩儿一把.目前已经成功在CentOS7.2上班成功安装 swift 4.0 https://github.co ...

  6. MySql学习笔记(四)

    MYSQL如何查看系统帮助: 1.查看官方API文档: http://dev.mysql.com/doc/ 2.通过Mysql中的help命令 比如:help create database MYSQ ...

  7. C语言_第一讲_C语言入门

    一.C语言的简介 1.C语言是一个标准,而执行标准的时候产生的自动化程序则是编译器2.了解:1983年美国国家标准化歇会(ANSI)制定了C语言标准.C语言的特点:3.代码的可移植性(理想状态是代码可 ...

  8. ansible批量分发免密钥登陆python脚本

    最近看了看强大的号称自动化运维的三大利器之一的--ansible,ok,亲测之后,确实感觉,对于我们这种DBA工作者来说,确实很受益. 值得注意的是ansible要求被管理服务器python版本不低于 ...

  9. MySQL之删_delete-truncate

    MySQL增删改查之删_delete-truncate 一.DELETE语句 删除数据记录 1.在单表中删除行 语法: DELETE [IGNORE] FROM tbl_name [WHERE whe ...

  10. 【机器学习PAI实践一】搭建心脏病预测案例

    一.背景 心脏病是人类健康的头号杀手.全世界1/3的人口死亡是因心脏病引起的,而我国,每年有几十万人死于心脏病. 所以,如果可以通过提取人体相关的体侧指标,通过数据挖掘的方式来分析不同特征对于心脏病的 ...