抱歉,我更新下邮箱地址。也可以加我微信(WillNi001),简历发送到我微信上,回复更及时。
谢谢~ 我会转告他们
好的,我等会把中文版的放在前面
测试设计和执行的时候还是要做的,万一有问题再修补代价就大了。我一般是参考开发的意见,但执行都是从产品使用角度来执行。
我觉得很多人都有一个想法/念头闪现过,就是改善测试行业。能够努力让更多的人认识到,测试人员是一个项目的标配。测试人员在项目中承担的责任和对项目的贡献是不低于开发人员的。包括刚进入这个行业的测试人员,可能也会被不重视测试的项目或管理者误导,以至于在没有完全了解测试行业所涉及、需要掌握的各种技术前,对自己的职业规划有了动摇。
我觉得这个社区有这么多资深的人,有必要整理一个测试领域的可视化的技能树(或许已经有了)。测试人员自己可能的发展方向,评估自己的兴趣方向,沿着某一分支或多个分支深入研究。
有了测试领域技能树的同时,让更多的人了解到:测试的每一项技能都是给项目的顺利进行增加了信心的;少了测试,项目风险是很大的。
有很长的路要走。
参数是可选的,你可以不加。直接用这个命令也可以。
python run.py 你的case
可以从 help 里面列出所有支持的参数,比如设置 loglevel
python run.py --loglevel DEBUG testcase
# 打印help
python run.py --help
我猜你这个问题是不是能够定位到元素,只是在点击的时候出现错误。你可以简单调试下,验证一下问题具体是出在什么地方。
# 插入断点调试 或者用python -m pdb 脚本文件
import pdb; pdb.set_trace()
dr.find_element_by_id('topLoginItem').click()
如果验证下来,能够定位元素但是点击有问题,十有八九是 Edge Webdriver 的问题。试用最新的 Edge Webdriver 或者用 Javascript 定位元素。先验证一下,再找解决办法。
这样的话,建议调试一下,手动尝试定位那个元素看看现象:
在 Selenium2Library/keywords/element.py 下面这个位置插入断点
def click_element(self, locator):
"""Click element identified by `locator`.
Key attributes for arbitrary elements are `id` and `name`. See
`introduction` for details about locating elements.
"""
self._info("Clicking element '%s'." % locator)
# debug
import pdb; pdb.set_trace()
self._element_find(locator, True, True).click()
用 robot/run.py 执行 case 进入调试模式
run.py 参数 case文件
在执行到 "Clicking element xpath=//*[@id="VC_SAM_SAMPLE"]"时,手动尝试用定位元素
pdb> element = self._element_find(locator, True, True)
pdb> dir(element)
pdb> element.click()
# 看看浏览器上元素是否被点击了
# 其他方式定位元素,多调试下
最终会结束吗?如果不是一直卡住而最终会结束的话,是有日志的。
我的思路还是 robotide/__init__.py 文件出错位置前插个断点,看下运行时的搜索路径。因为从 ride 的代码逻辑上看,import wx 时捕获到异常,就是说你单独执行 ride 命令 (相当于/usr/bin/env python ride) 的时候,找不到 “/usr/local/lib/wxPython-unicode-2.8.12.1/lib/python2.7”。我的猜测是只查找了 “/System/Library/Frameworks/Python.framework/Versions/2.7”。但是需要加上断点打印一下。
你用"python"(或者指定绝对路径的 python) 打开解释器时,sys.path 是有 wxPython 的。你可以试试指定解释器: python ride。但是上面的办法是最快最直接的。
看到这些前瞻性的测试想法并工程化,受益很多。希望整个测试行业都受益。
是比较诡异,是否可以进一步 debug,在 robotide/__init__.py 报错的位置前设置断点,看一下 library 查找路径是否有问题
# robotide/__init__.py
# 插入断点 - 调试完删除
import pdb; pdb.set_trace()
print sys.path
try:
import wx
except ImportError as e:
if "no appropriate 64-bit architecture" in e.message.lower() and \
sys.platform == 'darwin':
print("python should be executed in 32-bit mode with wxPython on OSX.")
else:
print(errorMessageTemplate.substitute(reason="wxPython not found."))
sys.exit(1)
性能测试及优化是比较复杂的,我的一些想法,一起讨论。
要分析罗列所有可能影响性能的业务逻辑和搜索对象的属性。哪些会影响性能,需要在分析完成后,有针对性的构造测试数据。另外还要考虑搜索结果在前端的展示。
放心吧,不是消失,是隐藏了。文件名以"."开头的在操作系统中有特殊用途。
# windows
dir /ah
# linux
ls -a
工作地点是上海吧
工作地点:杭州市西溪园区
driver.keyevent(xx)
def keyevent(self, keycode, metastate=None):
"""Sends a keycode to the device. Android only. Possible keycodes can be
found in http://developer.android.com/reference/android/view/KeyEvent.html.
:Args:
- keycode - the keycode to be sent to the device
- metastate - meta information about the keycode being sent
"""
这个你要看一下你的 appium python client 版本,master 分支 11 月 10 号以后的 python client 加了 TOUCH_ID.
Added touchId to driver (#143)
Added touchId to driver
Wrote a test for it (still need help running Python tests though). Updated capabilities to use iOS 10.1
commit Added touchId to driver
@@ -49,6 +49,7 @@ class MobileCommand(object):
END_TEST_COVERAGE = 'endTestCoverage'
LOCK = 'lock'
SHAKE = 'shake'
+ TOUCH_ID = 'touchId'
RESET = 'reset'
HIDE_KEYBOARD = 'hideKeyboard'
REPLACE_KEYS = 'replaceKeys'
是指 import xxx 之前,setProperty 可以用吗?我一般是这么用
props.put("test", vars.get("rowNum"));
props.put("test", vars.get("rowNum") + 3);
// CTRL+V (event_code 50 KEYCODE_V)
driver.pressKeyCode(50, AndroidKeyMetastate.META_CTRL_ON);
String PROXY = "localhost";
int PORT = 8080;
com.google.gson.JsonObject json = new com.google.gson.JsonObject();
json.addProperty("proxyType", "MANUAL");
json.addProperty("httpProxy", PROXY);
json.addProperty("httpProxyPort", PORT);
json.addProperty("sslProxy", PROXY);
json.addProperty("sslProxyPort", PORT);
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability("proxy", json);
GeckoDriverService service =new GeckoDriverService.Builder(firefoxBinary)
.usingDriverExecutable(new File("path to geckodriver"))
.usingAnyFreePort()
.usingAnyFreePort()
.build();
service.start();
// GeckoDriver currently needs the Proxy set in RequiredCapabilities
driver = new FirefoxDriver(service, cap, cap);
可以借鉴的例子
browsermob-proxy allows you to manipulate HTTP requests and responses, capture HTTP content, and export performance data as a HAR file.
提升文本框输入速度,可以用这种方式
element = driver.findElement(By.id("com.calculator:id/EditText01"));
element.click();
Process p = Runtime.getRuntime().exec("/path/of/adb -s emulator-8088 shell input text 123456");
对于你提到的这个办法,需要先从剪贴板读取再赋值
// 安装clipper,启动服务
// 发送到剪贴板
Process p = Runtime.getRuntime().exec("adb shell am broadcast -a clipper.set -e text 123456");
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
// 确定剪贴板内容是想要输入的
// 赋值 CTRL+V (event_code 50 KEYCODE_V)
driver.pressKeyCode(50, AndroidKeyMetastate.META_CTRL_ON);
没有看出明显的问题。是否可以加个 click 操作试一下。如果不行的话,建议升级 appium 后再试试。
element = EBC_Emulator_Data.driver.find_element_by_id("com.sdwx.ebochong:id/et_license_plate")
element.click()
element.send_keys("a13579")
info: [debug] [BOOTSTRAP] [debug] Attempting to clear using UiObject.clearText().
info: [debug] [BOOTSTRAP] [debug] Text remains after clearing, but it appears to be hint text.
info: [debug] [BOOTSTRAP] [debug] Text not cleared. Assuming remainder is hint text.
info: [debug] [BOOTSTRAP] [debug] Sending plain text to element: a13579
info: [debug] [BOOTSTRAP] [debug] Returning result: {"status":0,"value":true}
方便把日志帖一下吗
分享下自己项目中对 json 处理的一些想法。
类 xpath 获取对应的值是很方便的,平时写 Selenium 的 case 大多用 xpath,在做 REST API 测试时,处理 json 一般是有两种办法:
按照 path 取值
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(s);
int id = root.at("/data/0/id").asInt();
String name = root.at("/data/0/name").asText();
定义 Python/Java 对象 mapping 到 json
在实际使用中,如果 json 结构和深度非常复杂,第一种办法在组装 request body 和解析 response body 时会很繁琐,并且代码不再那么整洁,所以后来大多用第二种方法。
{
"type": "ServerProfileV6",
"name": "Profile101",
"serverHardwareUri": "/rest/server-hardware/{serverUUID}",
"affinity": "Bay",
"macType": "Virtual",
"serialNumberType": "Virtual",
"wwnType": "Virtual",
"hideUnusedFlexNics":true,
"connections": [{
"id": 1,
"name":"connection1",
"functionType": "Ethernet",
"portId": "Flb 1:1-a",
"requestedMbps": 2500,
"networkUri": "/rest/ethernet-networks/{networkUUID}",
"boot": {
"priority": "Primary"
}
},
{
"id": 2,
"functionType": "Ethernet",
"portId": "Auto",
"requestedMbps": 2500,
"networkUri": "/rest/network-sets/{networkSetUUID}",
"boot": {
"priority": "Secondary"
}
},
{
"id": 3,
"functionType": "FibreChannel",
"portId": "Auto",
"requestedMbps": 2500,
"networkUri": "/rest/fc-networks/{fcNetworkID}",
"boot": {
"priority": "Primary",
"bootVolumeSource": "UserDefined",
"targets": [{
"arrayWwpn": "{arrayWwpn}",
"lun": "{lun}"
}]
}
},
{
"id": 4,
"functionType": "Ethernet",
"portId": "Auto",
"requestedMbps": 2500,
"macType": "UserDefined",
"mac": "12:11:11:11:00:00",
"networkUri": "/rest/network-sets/{networkSetUUID}",
"boot": {
"priority": "NotBootable"
}
},
{
"id": 5,
"functionType": "FibreChannel",
"portId": "Auto",
"requestedMbps": 2500,
"wwpnType":"UserDefined",
"wwnn":"10:00:1C:11:00:00:00:00",
"wwpn":"10:00:1C:11:00:00:00:01",
"macType":"UserDefined",
"mac":"12:11:11:00:00:00",
"networkUri": "/rest/fc-networks/{fcNetworkID}",
"boot": {
"priority": "Secondary",
"bootVolumeSource": "UserDefined",
"targets": [{
"arrayWwpn": "{arrayWwpn}",
"lun": "{lun}"
}]
}
}],
"boot": {
"manageBoot": true,
"order": ["PXE",
"HardDisk",
"CD",
"Floppy",
"USB"]
},
"bios": {
"manageBios": true,
"overriddenSettings": [{
"id": "91",
"value": "1"
},
{
"id": "158",
"value": "2"
}]
},
"localStorage": {
"sasLogicalJBODs": [
{
"id": 1,
"deviceSlot": "Mezz 1",
"name": "Data Storage",
"numPhysicalDrives": 1,
"driveMinSizeGB": 200,
"driveMaxSizeGB": 600,
"driveTechnology": "SasHdd",
"sasLogicalJBODUri": null
},
{
"id": 2,
"deviceSlot": "Mezz 1",
"name": "Recovery Volume",
"numPhysicalDrives": 2,
"driveMinSizeGB": 200,
"driveMaxSizeGB": 600,
"driveTechnology": "SasHdd",
"sasLogicalJBODUri": null
}],
"controllers": [
{
"deviceSlot": "Embedded",
"mode": "RAID",
"initialize": false,
"importConfiguration": false,
"logicalDrives": [
{
"name": "Operating System",
"raidLevel": "RAID1",
"bootable": true,
"numPhysicalDrives": 2,
"driveTechnology": null,
"sasLogicalJBODId": null
}]
},
{
"deviceSlot": "Mezz 1",
"mode": "RAID",
"initialize": false,
"importConfiguration": false,
"logicalDrives": [
{
"name": null,
"raidLevel": "RAID0",
"bootable": false,
"numPhysicalDrives": null,
"driveTechnology": null,
"sasLogicalJBODId": 1
},
{
"name": null,
"raidLevel": "RAID1",
"bootable": false,
"numPhysicalDrives": null,
"driveTechnology": null,
"sasLogicalJBODId": 2
}]
}]
},
"firmware": {
"manageFirmware": true,
"firmwareBaselineUri": "/rest/firmware-drivers/{fwBaselineId}",
"forceInstallFirmware": false
}
}
看了下这个插件,master 和 slave encoding 不一致可能会导致类似问题。
hudson/plugins/maskpasswords/MaskPasswordsOutputStream.java
// TODO: The logic relies on the default encoding, which may cause issues when master and agent have different encodings
@SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "Open TODO item for wider rework")
@Override
protected void eol(byte[] bytes, int len) throws IOException {
String line = new String(bytes, 0, len);
if(passwordsAsPattern != null) {
line = passwordsAsPattern.matcher(line).replaceAll(MASKED_PASSWORD);
}
logger.write(line.getBytes());
}
我推测的解决办法 (手头上没有测试环境模拟你的问题,我的生产环境 Jenkins encoding 为 UTF-8 的情况下没有此问题):
在Manage Jenkins - System Information中查看