Appium 新手学 appium-python unittest 如何只运行一次配置项,其他函数按顺序来操作

· July 06, 2015 · Last by lanyou replied at March 14, 2017 · 6876 hits

昨晚凌晨3点有人撬我锁,还好我机智,睡之前都锁着的。不然被谋杀了,今天就不能更新帖子了。

狄更斯说:这是最好的时代,也是最坏的时代。我举双手同意。

言归正传,在使用appium,或者你用python来写自动化脚本过程中。有时候我们想只运行一次配置项,appium是desired_caps的一系列配置。webdriver可能是浏览器初始化的配置。
思考如何把N个测试函数按顺序来进行衔接。
而我们会很苦恼,如果用setup()跟teardown()来进行配置,我想如果你跟我一样实践过,会遇到每个函数都会调用setup()跟teardown(),也就是说,setup()里你写了desired_caps的一系列配置,你很有可能需要重启app。这对于我们想优化自动化脚本速度和想一连串运行函数的优质IT青年而言,简直不能忍
经过花了大量时间搜索以及跟同事的讨论,发现利器,setUpClass()跟tearDownClass()。或许我之前没深入了解unittest的函数的原因。接下来给demo。本demo为模拟器自带的拨号程序
# -*- coding: utf-8 -*-
#测试报告
import os
import unittest,sys,time,re,datetime,HTMLTestRunner
from appium import webdriver
from time import sleep
import sys
#reload(sys)
#sys.setdefaultencoding('utf-8')
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)


class ContactsAndroidTests(unittest.TestCase):

@classmethod
def setUpClass(cls):
print 'setUpClass'
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.4'
desired_caps['deviceName'] = '192.168.56.101:5555'
desired_caps['appPackage'] = 'com.android.dialer'
desired_caps['appActivity'] = '.DialtactsActivity'

cls.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

@classmethod
def tearDownClass(cls):
cls.driver.close_app()
cls.driver.quit()
print 'tearDownClass'

def setUp(self):
print "setup"

def tearDown(self):
print 'teardown'

def test_add_contacts(self):
print 1
#def test_B(self):
self.driver.find_element_by_id('com.android.dialer:id/call_history_button').click()

def test_A(self):
print 2
self.driver.find_element_by_class_name("android.app.ActionBar$Tab").click()


if __name__ == '__main__':
#unittest.main(exit=False)
suite = unittest.TestSuite()
suite.addTest(ContactsAndroidTests("test_add_contacts"))
suite.addTest(ContactsAndroidTests("test_A"))
#suite.addTest(IposCase("testmaters"))
timestr = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
filename = "D:\\appium\\appiumresult\\result_" + timestr + ".html"
print (filename)
fp = open(filename, 'wb')
runner = HTMLTestRunner.HTMLTestRunner(
stream=fp,
title='测试结果',
description='测试报告'
)
#suite = unittest.TestLoader().loadTestsFromTestCase(ContactsAndroidTests)
#unittest.TextTestRunner(verbosity=2).run(suite)
runner.run(suite)
#g_browser.quit()
fp.close() #测试报告关闭
而我的测试报告如下图


ps:最近睡觉都在思考如何解决脚本上的问题,睡眠质量好差

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 87 条回复 时间 点赞

请问这个报告是用HTMLTestRunner吗?

#2 · July 07, 2015 作者

#1楼 @halo_lan 代码很明显了。。

额, unittest 的官方文档里是有提到 setUpClass() 这些的,而且 unittest 可以用到的方法远不止 setUp,tearDown 这些。

学习 unittest 最快的路还是去把官方文档都看一遍,不求都会用,但至少需要知道有哪些方法可以用,直接实战+google 的方法是流沙上建房子,埋的坑只会越来越多。

之前不是跟你说了,用java可以秒杀吗? 还有你这测试报告给你们主管看,主管看了不会影响他的审美观吗?

#5 · July 07, 2015 作者

#4楼 @tspring 为什么会影响审美观,testng的测试报告我也没觉得多好看

6Floor has been deleted
#7 · July 09, 2015 作者

#6楼 @123456_ 真心不想看你的代码

#10 · July 13, 2015 作者

#9楼 @yangxiangfu 呵呵 我干嘛。。。

我执行了不行呢,提示没有定义drvier

#13 · July 14, 2015 作者

#12楼 @test882012 哎,你先把基础打好吧

楼主,问下你用的IDE是啥

#15 · July 31, 2015 作者

#14楼 @tasidingge pycharm啊

写的非常好

把set_up的方法里面的内容拿到上面类的外面去,作为一个全局变量。不封装在函数和类内

#18 · August 03, 2015 作者

#17楼 @julian1610 。。。。

我的测试报告,python的,应该美观点


#20 · August 11, 2015 作者

#19楼 @strayeagle 牛逼,怎么做的

Traceback (most recent call last):
File "test_1.py", line 66, in <module>
fp = open(filename, 'wb')
FileNotFoundError: [Errno 2] No such file or directory: 'E:\\testspace\\appiumresult\\result_2015102

求解....

#22 · October 23, 2015 作者

#21楼 @tlbin 你这个文件夹要先手工创建的

cls.driver和 self.driver 不是一个对象,可以这么用么?

#21楼 @turinblueice 哦,可以,instance如果自己不对属性赋值的话,继承类的同名属性。

#25 · November 13, 2015 作者

#24楼 @turinblueice 。。。。。自言自语?

#26 · November 13, 2015 作者

#24楼 @turinblueice 你qq号多少啊

#26楼 @mads 确实是要创建一个文件夹 但又开始报py3 引入HTMLTestTunner的错了

Traceback (most recent call last):
File "test_1.py", line 74, in <module>
runner.run(suite)
File "E:\python\lib\HTMLTestRunner.py", line 628, in run
test(result)
File "E:\python\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "E:\python\lib\unittest\suite.py", line 114, in run
self._handleClassSetUp(test, result)
File "E:\python\lib\unittest\suite.py", line 170, in _handleClassSetUp
self._addClassOrModuleLevelException(result, e, errorName)
File "E:\python\lib\unittest\suite.py", line 216, in _addClassOrModuleLevelException
result.addError(error, sys.exc_info())
File "E:\python\lib\HTMLTestRunner.py", line 584, in addError
output = self.complete_output()
File "E:\python\lib\HTMLTestRunner.py", line 558, in complete_output
return self.outputBuffer.getvalue()
AttributeError: '_TestResult' object has no attribute 'outputBuffer'
#28 · November 13, 2015 作者

#27楼 @tlbin 换2.7试试。。。

#30 · November 13, 2015 作者

#29楼 @tlbin 我不用3的。。。用2.7

#25楼 @mads 刚接触这个,所以研究研究,这个类成员和对象成员在python里面和其他c、c++不太一样

#33 · November 13, 2015 作者

#32楼 @turinblueice 怎么私信。。。

34Floor has been deleted

我也刚刚接触。。看你的DEMO感觉好深奥哦。。我朋友说这个框架相对于简单好用,是真的么?

@mads
我是python 2.7 。但是执行测试时,也报了这个错误:
AttributeError: '_TestResult' object has no attribute 'outputBuffer'

请问,是什么原因呢,怎么解决呢。排错了很久,也无解
谢谢

#27楼 @tlbin
我是python 2.7 。但是执行测试时,也报了和你一样同样的错误:
AttributeError: '_TestResult' object has no attribute 'outputBuffer'

请问,你是怎么解决的呢。我排错了很久,也无解
谢谢

#37楼 @xiaoxu790 我扔那了 还没管····你py2 可以找楼主 哈哈 @mads

python3-----修改下HTMLTestRunner源码-http://www.bubuko.com/infodetail-529431.html

#40 · January 04, 2016 作者

#39楼 @new 你玩吧,我N久不玩了

#39楼 @new 我就这么改的 然后还是之前我贴图的错

42Floor has been deleted

你好,我是这么做的,但是只运行了setUpClass和tearDownClass,用例都没执行

不知道是什么原因啊

#44 · January 05, 2016 作者

#43楼 @xzhan 把我代码贴过去看看。。。

#44楼 @mads 我就是贴的你的代码,只不过把参数和用例改成了我的。。

#46 · January 05, 2016 作者

#45楼 @xzhan 用例名是谁的。。。你的还是我的

#46楼 @mads 开始改的我自己的,后来试过完全用你的,都不行。。我用的python3.4,查了下unittest文档, setUP和tearDown这块和2没什么区别吧?

#48 · January 05, 2016 作者

#47楼 @xzhan 我用的是2.7,不用3

AttributeError: '_TestResult' object has no attribute 'outputBuffer' 的错有人解了吗。。。

#49楼 @kesha0 你这个错误解决了吗

#50楼 @mymgbaby 解了,不用setUpClass,直接把初始化的语句写在Class里面

#51 @kesha0 class AppKeyWord(unittest.TestCase, AppExecuteKeyword.Key,Driver.MyDriver,AppiumServer.AppiumServer):
@classmethod
def setUpClass(cls):
print 'setUpClass'
cls.driver =cls.get_driver()

@classmethod
def tearDownClass(cls):
cls.driver.close_app()
cls.driver.quit()

print 'tearDownClass'

def setUp(self):

# 在测试过程中打开任意活动
# driver.start_activity(app_package, app_activity)
self.verificationErrors = []

def action(self,case_id, case_name):
self.AppExecute(case_id, case_name)
print "finish case"

@staticmethod
def getTestFunc(case_id, case_name):
def func(self):
self.action(case_id, case_name)
return func

def tearDown(self):
print "-"*70
print u"用例运行成功"
time.sleep(5)
self.assertEqual([], self.verificationErrors)

没懂你的意思,这个是我的初始化代码,要怎么改

#52楼 @mymgbaby
亲测不用setUpClass就没问题,原因是什么我也不清楚

# -*- coding: utf-8 -*-
#测试报告
import os
import unittest,sys,time,re,datetime,HTMLTestRunner
from appium import webdriver
from time import sleep
import sys
#reload(sys)
#sys.setdefaultencoding('utf-8')
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)


class ContactsAndroidTests(unittest.TestCase):

desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.4'
desired_caps['deviceName'] = '192.168.56.101:5555'
desired_caps['appPackage'] = 'com.android.dialer'
desired_caps['appActivity'] = '.DialtactsActivity'
cls.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

def setUp(cls):
cls.driver.start_avtivity('com.android.dialer','.DialtactsActivity')

def tearDown(cls):
print 'teardown'

def test_add_contacts(cls):
print 1
#def test_B(cls):
cls.driver.find_element_by_id('com.android.dialer:id/call_history_button').click()

def test_A(cls):
print 2
cls.driver.find_element_by_class_name("android.app.ActionBar$Tab").click()


if __name__ == '__main__':
#unittest.main(exit=False)
suite = unittest.TestSuite()
suite.addTest(ContactsAndroidTests("test_add_contacts"))
suite.addTest(ContactsAndroidTests("test_A"))
#suite.addTest(IposCase("testmaters"))
timestr = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
filename = "D:\\appium\\appiumresult\\result_" + timestr + ".html"
print (filename)
fp = open(filename, 'wb')
runner = HTMLTestRunner.HTMLTestRunner(
stream=fp,
title='测试结果',
description='测试报告'
)
#suite = unittest.TestLoader().loadTestsFromTestCase(ContactsAndroidTests)
#unittest.TextTestRunner(verbosity=2).run(suite)
runner.run(suite)
#g_browser.quit()
fp.close() #测试报告关闭
ContactsAndroidTests.driver.quit()

#19楼 @strayeagle 报告模版自己写的么?

#55 · June 16, 2016 作者

#54楼 @silentteamo import HTMLTestRunner。。。

请问我按你的来了但是 出现这样的错误

#57 · July 12, 2016 作者

#56楼 @y693055797 哎,你是不是用selenium的包了

#57楼 @mads 是的 换成appium的包 我试试

#57楼 @mads 是不是这种方法 中间一个用例处错误后 后面的就无法继续进行了 全部报错的

#60 · July 12, 2016 作者

#59楼 @y693055797 不会啊,错误是啥错误哦

#60楼 @mads 就是 我中间有一个assert的 没通过 这样
他就直接 跳出来 停止测试了 后来我把 这句断言删了

#62 · July 12, 2016 作者

#61楼 @y693055797 你是啥意思。单个函数的assert就是这么设计的,失败接下来的步骤也不能运行了。但是不影响接下来的其他函数

#62楼 @mads 接下来的 def中的用例 也不运行 是正确的吗 那我想实现 这个不通过 但是我想让他继续进行下一个用例 (保证下一个的用例还是能够进行的比如 控件仍然能点击) 这样能实现吗

#64 · July 12, 2016 作者

#63楼 @y693055797 接下来的用例不运行,就是你自己脚本的问题了。我是可以继续运行的

#65 · July 12, 2016 作者

#63楼 @y693055797 因为是继续上一次失败的UI的操作,所以你得考虑上一次失败后如何跟下一个用例进行衔接

#65楼 @mads 不过我现在又出现问题 突然脚本打开activity后 不继续进行了
一直停在主界面 而且 cls.driver.close_app() 这也出现错误 ,,

#67 · July 13, 2016 作者

#66楼 @y693055797 啥意思。。。重启电脑重新跑

#67楼 @mads 重启过了 就是打开 activity后 不继续按我写的脚本点击 就一直停在那 过一会才会提示出错
WebDriverException: Message: UiAutomator died while responding to command, please check appium logs!

#64楼 @y693055797 就显示这 不往下点击

#70 · July 13, 2016 作者

#69楼 @y693055797 。。我咋知道。。你应该把代码贴出来,报错贴出来有啥用。。

#70楼 @mads #-- coding: UTF-8 --

#从这里开始 这是我敲的
import os
import unittest,time,re,datetime,HTMLTestRunner
from appium import webdriver
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

PATH=lambda p:os.path.abspath(
os.path.join(os.path.dirname(file),p)

)

class LoginAndroidTests(unittest.TestCase):

@classmethod
def setUpClass(cls):
print 'setUpClass'
desired_caps={}
desired_caps['device'] = 'android'
desired_caps['platformName']='Android'
desired_caps['browserName']=''
desired_caps['version']='5.1.1'
desired_caps['deviceName']='061f49d4e302964a'
desired_caps['appPackage']='xxxxx'
desired_caps['appActivity']='xxxx.ui.screen.HomeActivity'
desired_caps['noSign'] = 'true'

cls.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

@classmethod
def tearDownClass(cls):
cls.driver.close_app()
cls.driver.quit()
print 'tearDownClass'

def setUp(self):
print "setup"

def tearDown(self):
print 'teardown'

def test_1login(self):
print 1
time.sleep(5)

name= self.driver.find_element_by_id('Id')
name.click()
name.send_keys('admin')

time.sleep(3)

这样的 就是不执行点击操作

#72 · July 13, 2016 作者

#71楼 @y693055797 能不能用代码块。。

#74 · July 13, 2016 作者

#73楼 @y693055797 你的if name == 'main':呢。。。

#75 · July 13, 2016 作者

#73楼 @y693055797 你最好再看下自己的代码结构,没有主函数的。。

#75楼 @mads 突然又好了 应该是我们公司的这个apk的毛病😹

#75楼 @mads 请问 appium 不支持有系统权限的apk吗 我发现我们公司这个apk只要配置成 油系统权限的后 就不能执行点击操作了

#19楼 @strayeagle 请问报告中case 的中文是怎么加上的

#79楼 @mads 就是生成的报告中 case带中文 如图

#81 · July 15, 2016 作者

"""啊啊啊“”“”

#83 · July 15, 2016 作者

#82楼 @y693055797 就这么写啊

def A():
"""啊啊啊啊啊啊啊啊"""

#83楼 @mads 哦哦 就注释啊 明白了 还有问题 请教下 我的pycharm 为什么 按ALT+SHIFT+F10后 没有自己的脚本名字的选项

#85 · July 15, 2016 作者

#84楼 @y693055797 我不按这个玩意的

#85楼 @mads 那 run unittests 就不会生成报告 。。。只会在这有,

#87 · July 15, 2016 作者

#86楼 @y693055797 来吧,来互相伤害

#87楼 @mads 😅 解决了 那个问题 不过我这里为什么有时候 用例结束后 不会关闭app,,

#89 · July 18, 2016 作者

#88楼 @y693055797 来吧,来互相伤害

strayeagle 回复

大神,你的报告源码可否共享一下?膜拜!💪

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up