如果按照上面代码运行的话,也没看出有问题,你确认一下 device1 或者 device2 在结束后是否返回
这里是你的代码
import time
import threading
def device1():
for i in range(10):
print('device1')
time.sleep(1)
def device2():
for i in range(10):
print('device2')
time.sleep(0.5)
threads = []
t1 = threading.Thread(target=device1)
threads.append(t1)
t2 = threading.Thread(target=device2)
threads.append(t2)
if __name__ == '__main__':
for t in threads:
print('start', t)
t.start()
for t in threads:
print('join', t)
t.join()
print('all end')
这里是上面代码对应的打印
我的理解是这样的
在 macaca 里,webdriver.py 中的 WebDriver 以及 webelement.py 中的 WebElement 这两个类中确实没有直接声明 element_by_xxx 相关的方法,而是通过 add_element_extension_method(WebDriver) 或者 add_element_extension_method(WebElement) 去声明的,所以 element_by_xxx 调用是没问题,但是 PyCharm 不认这个。我想 wd.py 的作者是为了是代码聚合度更高才采用这样的方式的吧
已加
不在
给你一个思路(Macaca+Python)
是的,封装的目的就是为了降低编写脚本的难度以及增强脚本的可维护性
我理解的分解是这样的,你的目的有两个:
1、需要滑动查找元素
2、判断查找到的元素的 text 是否为某个目标值
所有,按照这个思路就应该分两步,通过诸如这类的方法 find_element_by_swipe_up 获取到 element,然后在用 element.text 去判断。
macaca 本身并没有封装滑动查找元素,这个通用方法是我自己写的。另外,还写了 wait_string、wait_string_use_and、wait_string_use_or、wait_element_by_accessibility_id、click_element_by_accessibility_id,不过这个要后面在看是否分享。
_find_element_by_swipe 是个内部方法,是为了给诸如下面的外部方法调用的(目的是返回一个 element,如果找到就直接返回了,不会多滑动的。在没有找到的时候且未超过滑动次数的时候才滑动,超过滑动次数就直接给异常,这里给异常也是符合 macaca 查找 element 逻辑规则的)
而你添加这个参数的目的是什么呢?
def find_element_by_swipe_up(self, using, value, element=None, steps=10, max_swipe=5):
"""
find element by swipe up
:param using: The element location strategy.
"id","xpath","link text","partial link text","name","tag name","class name","css selector"
:param value: The value of the location strategy.
:param element: WebElement of Macaca, if None while swipe window of phone
:param steps: steps of swipe for Android, The lower the faster
:param max_swipe: the max times of swipe
:return: WebElement of Macaca
Raises:
WebDriverException.
"""
return self._find_element_by_swipe('up', using, value,
element=element, steps=steps, max_swipe=max_swipe)
多谢达峰老师推荐!
要的就是这效果
赞
很棒啊!
后面的话,可以把 path = '/Users/zhaozhiquan/automation/iOSSdk/result/' + day + '/screencap'这里面的绝对路径修改一下,这样他人使用你的脚本的时候才不会有问题。
我的报告和截图是放在同一目录下的
如下,path = ReportPath().get_path() + '\' + screenshot 中的 ReportPath().get_path() 就是报告的存放路径(同时也是截图的存放路径)。通过 raise WebDriverException(message=flag + screenshot(func.qualname_)) 这样的异常,在异常信息中把截图名称给出(用 flag 变量中的'IMAGE:'标记,截图名为 screenshot = name + '-' + date_time + '.PNG')。
flag = 'IMAGE:'
def _screenshot(name):
date_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
screenshot = name + '-' + date_time + '.PNG'
path = ReportPath().get_path() + '\\' + screenshot
driver = BasePage().get_driver()
driver.save_screenshot(path)
return screenshot
def teststep(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
log.i('\t--> %s', func.__qualname__)
ret = func(*args, **kwargs)
return ret
except WebDriverException:
log.e('\t<-- %s, %s', func.__qualname__, 'Error')
raise WebDriverException(message=flag + _screenshot(func.__qualname__))
return wrapper
由于上面的异常信息中已经给出了截图的名称,因此,在 HTMLTestRunner.py 中 image = image[image.find("IMAGE:")+6:(int(image.find("PNG"))+3)] 就可以把截图名拿出来(截图后缀为.PNG)。这样的话,因为报告和截图在同一个目录下,所以这里只需要知道截图名称就可以。
def _generate_report_test(self, rows, cid, tid, n, t, o, e):
# e.g. 'pt1.1', 'ft1.1', etc
has_output = bool(o or e)
tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
name = t.id().split('.')[-1]
doc = t.shortDescription() or ""
desc = doc and ('%s: %s' % (name, doc)) or name
tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
# o and e should be byte string because they are collected from stdout and stderr?
if isinstance(o,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# uo = unicode(o.encode('string_escape'))
uo = e
else:
uo = o
if isinstance(e,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# ue = unicode(e.encode('string_escape'))
ue = e
else:
ue = e
script = self.REPORT_TEST_OUTPUT_TMPL % dict(
id = tid,
output = saxutils.escape(uo),
# output=saxutils.escape(uo + ue),
)
image = self.REPORT_TEST_OUTPUT_IMAGE % dict(
screenshot = saxutils.escape(uo)
# screenshot = saxutils.escape(uo + ue)
)
caseid = self.REPORT_TEST_OUTPUT_CASEID % dict(
case_id = saxutils.escape(uo)
# case_id = saxutils.escape(uo + ue)
)
row = tmpl % dict(
tid = tid,
Class = (n == 0 and 'hiddenRow' or 'none'),
style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
desc = desc,
script = script,
image = image[image.find("IMAGE:")+6:(int(image.find("PNG"))+3)],
caseid = caseid[caseid.find("case"):(int(caseid.find("case"))+9)],
status = self.STATUS[n],
)
rows.append(row)
if not has_output:
return
android sdk 的安装路径有空格的话,也会有问题的
#20 楼 @342164796 差不多,我弄的多进程,效果还可以。有需要可以一起探讨
#18 楼 @342164796 我在装饰器同时加了打印的,效果不错,可以用这种方式重构来看看
我提供一下我的截图思路(Macaca + HTMLTestRunner):
1、在元素未找到时截图
2、在用例失败时截图
代码如下:
1、每个测试步骤加上@teststep这个装饰器
2、每条用例加上@testcase这个装饰器
def _screenshot(name):
date_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
screenshot = name + '-' + date_time + '.PNG'
path = ReportPath().get_path() + '\\' + screenshot
driver = BasePage().get_driver()
driver.save_screenshot(path)
return screenshot
def teststep(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
log.i('\t--> %s', func.__qualname__)
ret = func(*args, **kwargs)
return ret
except WebDriverException:
log.e('\t<-- %s, %s', func.__qualname__, 'Error')
raise WebDriverException(message=flag + _screenshot(func.__qualname__))
return wrapper
def testcase(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
log.i('--> %s', func.__qualname__)
ret = func(*args, **kwargs)
log.i('<-- %s, %s\n', func.__qualname__, 'Success')
return ret
except WebDriverException:
log.e('<-- %s, %s\n', func.__qualname__, 'Error')
raise WebDriverException
except AssertionError:
log.e('<-- %s, %s\n', func.__qualname__, 'Fail')
raise AssertionError(flag + _screenshot(func.__qualname__))
return wrapper
测试步骤的代码如下:
@teststep
def input_account(self, account):
"""以“请输入手机号码”的TEXT为依据"""
self.driver\
.element_by_name('请输入手机号码')\
.clear()\
.send_keys(account)
测试用例的代码如下:
@testcase
def test_Car_MyCarInsurEntry_Func_010(self):
"""我的车险入口验证"""
self.home_page.click_my()
login = LoginPage()
if login.wait_page():
login.input_account(VALID_ACCOUNT.account())
login.input_password(VALID_ACCOUNT.password())
login.login()
gesture = GesturePasswordPage()
if gesture.wait_page():
gesture.skip()
if self.home_page.wait_page():
self.home_page.click_my()
my_page = PlatformAppMyPage()
my_page.wait_page()
my_page.click_my_car_insurance()
my_car_insurance = MyCarInsurancePage()
self.assertTrue(my_car_insurance.wait_page())