From: https://blog.csdn.net/site008/article/details/77622472html
前言
(python基础比较弱的,建议你们多花点时间把基础语法学好,这里有套视频,能够照着练习下:http://pan.baidu.com/s/1i44jZdb 密码:92fs)
熟悉java的应该都清楚常见的单元测试框架Junit和TestNG,这个招聘的需求上也是常常见到的。python里面也有单元测试框架-unittest,至关因而一个python版的junit。
python里面的单元测试框架除了unittest,还有一个pytest框架,这个用的比较少,后面有空再继续分享。java
1).先导入unittestpython
2).用help函数查看源码解析
3).查看描述:
Python unit testing framework, based on Erich Gamma's JUnit and KentBeck's Smalltalk testing framework.
翻译:python的单元测试框架,是基于java的junit测试框架。git
1).能够把上图的这段代码copy出来,单独运行,看看测试结果。web
Simple usage:express
import unittest class IntegerArithmeticTestCase(unittest.TestCase): deftestAdd(self): ## test method names begin 'test*' self.assertEqual((1 + 2), 3) self.assertEqual(0 + 1, 1) deftestMultiply(self): self.assertEqual((0 * 10), 0) self.assertEqual((5 * 8), 40) if __name__ == '__main__': unittest.main()
2).第一行是导入unittest这个模块
3).class这一行是定义一个测试的类,并继承unittest.TestCase这个类
4).接下来是定义了两个测试case名称:testAdd和testMultiply
5).注释里面有句话很重要,这个要敲下黑板记笔记了:## test method names begin 'test*'
--翻译:测试用例的名称要以test开头
6).而后是断言assert,这里的断言方法是assertEqual-判断两个是否相等,python3.x
这个断言能够是一个也能够是多个
7).if下面的这个unittest.main()是运行主函数,运行后会看到测试结果(跑了两个用例耗时0.000秒,两个用例都经过):
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK浏览器
1).上面的两个案例是加法和乘法,咱们能够写个case试下减法和除法。
2).有不少小伙伴不知道断言怎么写,断言其实就是拿实际结果和指望结果去对比,对比的方法不少,这里只是举的最简单的一个判断相等的方法。服务器
3).最后运行结果,第二个是失败的,失败缘由:AssertionError: 3 != 3.5
F.
======================================================================
FAIL: testDivide (__main__.Test)并发
这里是测试除法
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:/test/web-project/p.py", line 14, in testDivide
self.assertEqual(result, hope)
AssertionError: 3 != 3.5
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
1).setUp:在写测试用例的时候,每次操做其实都是基于打开浏览器输入对应网址这些操做,这个就是执行用例的前置条件。
2).tearDown:执行完用例后,为了避免影响下一次用例的执行,通常有个数据还原的过程,这就是执行用例的后置条件。
3).不少人执行完用例,都不去作数据还原,以至于下一个用例执行失败,这就是不喜欢擦屁股的事情,习惯很差。
4).前置和后置都是非必要的条件,若是没有也能够写pass
1).打开博客首页为例,写一个简单的case
2).判断title彻底等于指望结果
3).运行经过,下面会有一个绿条显示:1 test passed
# coding=utf-8 from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC import time import unittest class Blog(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.get("http://www.cnblogs.com/yoyoketang") def test_blog(self): time.sleep(3) result = EC.title_is(u'上海-悠悠 - 博客园')(self.driver) print result self.assertTrue(result) def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
前言
不少初学者在使用unittest框架时候,不清楚用例的执行顺序究竟是怎样的。对测试类里面的类和方法分不清楚,不知道何时执行,何时不执行。
本篇经过最简单案例详细讲解unittest执行顺序。
1、案例分析
1.先定义一个测试类,里面写几个简单的case
# coding:utf-8 import unittest import time class Test(unittest.TestCase): def setUp(self): print "start!" def tearDown(self): time.sleep(1) print "end!" def test01(self): print "执行测试用例01" def test03(self): print "执行测试用例03" def test02(self): print "执行测试用例02" def addtest(self): print "add方法" if __name__ == "__main__": unittest.main()
2、执行结果
D:\test\python2\python.exe D:/test/test01.py
start!
执行测试用例01
end!
start!
执行测试用例02
end!
start!
执行测试用例03
end!
.
----------------------------------------------------------------------
Ran 3 tests in 3.001s
OK
3、结果分析
1.执行顺序:
start!-执行测试用例01-end!
start!-执行测试用例02-end!
start!-执行测试用例03-end!
2.从执行结果能够看出几点:
--先执行的前置setUp,而后执行的用例(test*),最后执行的后置tearDown。
--测试用例(test*)的执行顺序是根据01-02-03执行的,也就是说根据用例名称来顺序执行的。
--addtest(self)这个方法没执行,说明只执行test开头的用例。
4、selenium实例
1.具体实例参考 登陆方法(参数化)
# coding:utf-8 from selenium import webdriver import unittest import time class Bolg(unittest.TestCase): u'''登陆博客''' def setUp(self): self.driver = webdriver.Firefox() url = "https://passport.cnblogs.com/user/signin" self.driver.get(url) self.driver.implicitly_wait(30) def login(self, username, psw): u'''这里写了一个登陆的方法,帐号和密码参数化''' self.driver.find_element_by_id("input1").send_keys(username) self.driver.find_element_by_id("input2").send_keys(psw) self.driver.find_element_by_id("signin").click() time.sleep(3) def is_login_sucess(self): u'''判断是否获取到登陆帐户名称''' try: text = self.driver.find_element_by_id("lnk_current_user").text print text return True except: return False def test_01(self): u'''登陆案例参考:帐号,密码本身设置''' self.login(u"上海-悠悠", u"xxxx") # 调用登陆方法 # 判断结果 result = self.is_login_sucess() self.assertTrue(result) def test_02(self): u'''登陆案例参考:帐号,密码本身设置''' self.login(u"上海-悠悠", u"xxxx") # 调用登陆方法 # 判断结果 result = self.is_login_sucess() self.assertTrue(result) def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
咱们在写用例的时候,单个脚本的用例好执行,那么多个脚本的时候,如何批量执行呢?这时候就须要用到unittet里面的discover方法来加载用例了。
加载用例后,用unittest里面的TextTestRunner这里类的run方法去一次执行多个脚本的用例。
1、新建测试项目
1.pycharm左上角File>New Projetc>Pure Python,在location位置命名一个测试工程的名称:yoyotest,而后保存
2.选中刚才新建的工程右键>New>Python Package>新建一个case文件夹
3.重复第2步的操做,新建一个case的文件夹,在里面添加一个baidu和一个blog的文件夹,里面分别有两个用例的脚本,以下图所示。
test_01,test_02,test_03,test_04是咱们写用例的脚本
4.test_01建立完后,打开脚本,写入用例
5.在yoyotest这个项目下面建立一个脚本run_all_case.py,接下来用这个脚本去批量执行全部的用例。
2、diascover加载测试用例
1.discover方法里面有三个参数:
-case_dir:这个是待执行用例的目录。
-pattern:这个是匹配脚本名称的规则,test*.py意思是匹配test开头的全部脚本。
-top_level_dir:这个是顶层目录的名称,通常默认等于None就好了。
2.discover加载到的用例是一个list集合,须要从新写入到一个list对象testcase里,这样就能够用unittest里面的TextTestRunner这里类的run方法去执行。
3.运行后结果以下,就是加载到的全部测试用例了:
<unittest.suite.TestSuite tests=[<baidu.test_01.Test testMethod=test01>, <baidu.test_01.Test testMethod=test02>, <baidu.test_01.Test testMethod=test03>, <baidu.test_02.Test
testMethod=test01>, <baidu.test_02.Test testMethod=test02>, <baidu.test_02.Test testMethod=test03>, <bolg.test_03.Test testMethod=test01>, <bolg.test_03.Test testMethod=test02>,
<bolg.test_03.Test testMethod=test03>, <bolg.test_04.Test testMethod=test01>, <bolg.test_04.Test testMethod=test02>, <bolg.test_04.Test testMethod=test03>]>
3、run测试用例
1.为了更方便的理解,能够把上面discover加载用例的方法封装下,写成一个函数
2.参考代码:
# coding:utf-8 import unittest import os # 用例路径 case_path = os.path.join(os.getcwd(), "case") # 报告存放路径 report_path = os.path.join(os.getcwd(), "report") def all_case(): discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None) print(discover) return discover if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(all_case())
前言
前面讲到unittest里面setUp能够在每次执行用例前执行,这样有效的减小了代码量,可是有个弊端,好比打开浏览器操做,每次执行用例时候都会从新打开,这样就会浪费不少时间。
因而就想是否是能够只打开一次浏览器,执行完用例再关闭呢?这就须要用到装饰器(@classmethod)来解决了。
1、装饰器
1.用setUp与setUpClass区别
setup():每一个测试case运行前运行
teardown():每一个测试case运行完后执行
setUpClass():必须使用@classmethod 装饰器,全部case运行前只运行一次
tearDownClass():必须使用@classmethod装饰器,全部case运行完后只运行一次
2.@是修饰符,classmethod是python里的类方法
2、执行顺序
1.用类方法写几个简单case,能够对比这篇:Selenium2+python自动化52-unittest执行顺序
# coding:utf-8 import unittest import time
class Test(unittest.TestCase): @classmethod def setUpClass(cls): print "start!" @classmethod def tearDownClass(cls): time.sleep(1) print "end!" def test01(self): print "执行测试用例01" def test03(self): print "执行测试用例03" def test02(self): print "执行测试用例02" def addtest(self): print "add方法"
if __name__ == "__main__": unittest.main()
2.从执行结果能够看出,前置和后置在执行用例前只执行了一次。
start!
执行测试用例01
执行测试用例02
执行测试用例03
...end!
----------------------------------------------------------------------
Ran 3 tests in 1.001s
3、selenium实例
1.能够把打开浏览器操做放到前置setUpClass(cls)里,这样就能够实现打开一次浏览器,执行多个case了
# coding:utf-8 from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC import unittest
class BolgHome(unittest.TestCase):
u'''博客首页''' @classmethod def setUpClass(cls): cls.driver = webdriver.Firefox() url = "http://www.cnblogs.com/yoyoketang/" cls.driver.get(url) cls.driver.implicitly_wait(30)
@classmethod def tearDownClass(cls): cls.driver.quit()
def test_01(self): u'''验证元素存在:博客园''' locator = ("id", "blog_nav_sitehome") text = u"博客园" result = EC.text_to_be_present_in_element(locator, text)(self.driver) self.assertTrue(result) def test_02(self): u'''验证元素存在:首页''' locator = ("id", "blog_nav_myhome") text = u"首页" result = EC.text_to_be_present_in_element(locator, text)(self.driver) self.assertTrue(result)
if __name__ == "__main__": unittest.main()
前言
批量执行完用例后,生成的测试报告是文本形式的,不够直观,为了更好的展现测试报告,最好是生成HTML格式的。
unittest里面是不能生成html格式报告的,须要导入一个第三方的模块:HTMLTestRunner
备注:(如下是python2.7的HTMLTestRunner,python3.x的HTMLTestRunner须要本身稍作修改,能够在这里下载:http://pan.baidu.com/s/1hs5OXNY)
1、导入HTMLTestRunner
1.这个模块下载不能经过pip安装了,只能下载后手动导入,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
2.Download下HTMLTestRunner.py文件就是咱们须要下载的包。
3.下载后手动拖到python安装文件的Lib目录下
2、demo解析
1.下载Download下的第二个文件test_HTMLTestRunner.py,这个就是官方给的一个测试demo了,从这个文件能够找到该模块的用法。
2.找到下图这段,就是官方给的一个demo了,test_main()里上半部分就是加载测试case,咱们不须要搞这么复杂。
参考前面一篇内容就好了Selenium2+python自动化53-unittest批量执行(discover)
3.最核心的代码是下面的红色区域,这个就是本篇的重点啦。
3、生成html报告
1.咱们只需把上面红色区域代码copy到上一篇的基础上稍作修改就能够了,这里主要有三个参数:
--stream:测试报告写入文件的存储区域
--title:测试报告的主题
--description:测试报告的描述
2.report_path是存放测试报告的地址
4、测试报告详情
1.找到测试报告文件,用浏览器打开,点开View里的Detail能够查看详情描述。
2.为了生成带中文描述的测试用例,能够在case中添加注释,如在test_01的脚本添加以下注释:
class Test(unittest.TestCase): def setUp(self): print "start!" def tearDown(self): time.sleep(1) print "end!" def test01(self): u'''测试登陆用例,帐号:xx 密码xx''' print "执行测试用例01" def test03(self): u'''测试登搜索用例,关键词:xxx''' print "执行测试用例03"
3.从新运行后查看测试报告
5、参考代码:
1.我下面的代码文件路径用的相对路径,这样就避免代码换个地址找不到路径的状况了
# coding:utf-8 import unittest import os import HTMLTestRunner # 用例路径 case_path = os.path.join(os.getcwd(), "case") # 报告存放路径 report_path = os.path.join(os.getcwd(), "report") def all_case(): discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None) print(discover) return discover if __name__ == "__main__": # runner = unittest.TextTestRunner() # runner.run(all_case()) # html报告文件路径 report_abspath = os.path.join(report_path, "result.html") fp = open(report_abspath, "wb") runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'自动化测试报告,测试结果以下:', description=u'用例执行状况:') # 调用add_case函数返回值 runner.run(all_case()) fp.close()
前言
python2用HTMLTestRunner生成测试报告时,有中文输出状况会出现乱码,这个主要是编码格式不统一,改下编码格式就行。
下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
1、中文乱码
1.测试报告中,msg自定义异常内容有中文状况会出现乱码,以下图所示
2、修改编码
1.找到HTMLTestRunner.py文件,搜索:uo =
2.找到红色区域设置编码的两个地方
3.注释掉红色区域这两个设置,从新添加编码格式为:uo = o.decode('utf-8') ue = e.decode('utf-8')
4.修改好以后记得保存,从新运行,乱码问题就解决了
3、python3报告问题
1.python3的小伙伴直接用这个下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html的文件,是不能直接生成报告的,须要稍作修改
2.修改后的源文件已经打包:https://files.cnblogs.com/files/zidonghua/HTMLTestRunner%28%E7%8B%AC%E5%AE%B6%E5%90%88%E9%9B%86%29.zip
(另外不少朋友在简单的示例代码中,生成不了测试报告,我来上传一份完整项目模板:https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C.rar)
前言
在测试用例中,执行完测试用例后,最后一步是判断测试结果是pass仍是fail,自动化测试脚本里面通常把这种生成测试结果的方法称为断言(assert)。
用unittest组件测试用例的时候,断言的方法仍是不少的,下面介绍几种经常使用的断言方法:assertEqual、assertIn、assertTrue
1).下面写了4个case,其中第四个是执行失败的
# coding:utf-8 import unittest class Test(unittest.TestCase): def test01(self): '''判断 a == b ''' a = 1 b = 1 self.assertEqual(a, b) def test02(self): '''判断 a in b''' a = "hello" b = "hello world!" self.assertIn(a, b) def test03(self): '''判断 a isTrue ''' a = True self.assertTrue(a) def test04(self): '''失败案例''' a = "上海-悠悠" b = "yoyo" self.assertEqual(a, b) if __name__ == "__main__": unittest.main()
2).执行结果以下
Failure
Expected :'\xe4\xb8\x8a\xe6\xb5\xb7-\xe6\x82\xa0\xe6\x82\xa0'
Actual :'yoyo'
<Click to see difference>
Traceback (most recent call last):
File "D:\test\yoyotest\kecheng\test12.py", line 27, in test04
self.assertEqual(a, b)
AssertionError: '\xe4\xb8\x8a\xe6\xb5\xb7-\xe6\x82\xa0\xe6\x82\xa0' != 'yoyo'
3.执行的结果,中文编码不对,没正常显示中文,遇到这种状况,能够自定义异常输出
1).以assertEqual为例分析:
assertEqual(self, first, second, msg=None)
Fail if the two objects are unequal as determined by the'=='
operator.
2).翻译:若是两个对象不能相等,就返回失败,至关于return: first==second
3).这里除了相比较的两个参数first和second,还有第三个参数msg=None,这个msg参数就是遇到异常后自定义输出信息
1).assertEqual(self, first, second,msg=None)
--判断两个参数相等:first == second
2).assertNotEqual(self, first, second,msg=None)
--判断两个参数不相等:first != second
3).assertIn(self, member, container,msg=None)
--判断是字符串是否包含:member in container
4).assertNotIn(self, member,container, msg=None)
--判断是字符串是否不包含:member not in container
5).assertTrue(self, expr, msg=None)
--判断是否为真:expr is True
6).assertFalse(self, expr, msg=None)
--判断是否为假:expr is False
7).assertIsNone(self, obj, msg=None)
--判断是否为None:objis None
8).assertIsNotNone(self, obj,msg=None)
--判断是否不为None:obj is not None
3.7.4 unittest全部断言方法
1).下面是unittest框架支持的全部断言方法,有兴趣的同窗能够慢慢看。(官方资料)
| assertAlmostEqual(self, first, second, places=None, msg=None,delta=None)
| Fail if the two objects are unequal asdetermined by their | difference rounded to the given number ofdecimal places | (default 7) and comparing to zero, or bycomparing that the | between the two objects is more than the givendelta. | | Note that decimal places (from zero) areusually not the same | as significant digits (measured from the mostsignficant digit). | | If the two objects compare equal then they willautomatically | compare almost equal. | | assertAlmostEquals = assertAlmostEqual(self, first, second,places=None, msg=None, delta=None) | | assertDictContainsSubset(self, expected, actual, msg=None) | Checks whether actual is a superset ofexpected. | | assertDictEqual(self, d1, d2, msg=None) | | assertEqual(self, first, second, msg=None) | Fail if the two objects are unequal asdetermined by the '==' | operator. | | assertEquals = assertEqual(self, first, second, msg=None) | | assertFalse(self, expr, msg=None) | Check that the expression is false. | | assertGreater(self, a, b, msg=None) | Just like self.assertTrue(a > b), but with anicer default message. | | assertGreaterEqual(self, a, b, msg=None) | Just like self.assertTrue(a >= b), but witha nicer default message. | | assertIn(self, member, container, msg=None) | Just like self.assertTrue(a in b), but with anicer default message. | | assertIs(self, expr1, expr2, msg=None) | Just like self.assertTrue(a is b), but with anicer default message. | | assertIsInstance(self, obj, cls, msg=None) | Same as self.assertTrue(isinstance(obj, cls)),with a nicer | default message. | | assertIsNone(self, obj, msg=None) | Same as self.assertTrue(obj is None), with anicer default message. | | assertIsNot(self, expr1, expr2, msg=None) | Just like self.assertTrue(a is not b), but witha nicer default message. | | assertIsNotNone(self, obj, msg=None) | Included for symmetry with assertIsNone. | | assertItemsEqual(self, expected_seq, actual_seq, msg=None) | An unordered sequence specific comparison. Itasserts that | actual_seq and expected_seq have the sameelement counts. | Equivalent to:: | | self.assertEqual(Counter(iter(actual_seq)), | Counter(iter(expected_seq))) | | Asserts that each element has the same count inboth sequences. | Example: | - [0, 1, 1] and [1, 0,1] compare equal. | - [0, 0, 1] and [0, 1]compare unequal. | | assertLess(self, a, b, msg=None) | Just like self.assertTrue(a < b), but with anicer default message. | | assertLessEqual(self, a, b, msg=None) | Just like self.assertTrue(a <= b), but witha nicer default message. | | assertListEqual(self, list1, list2, msg=None) | A list-specific equality assertion. | | Args: | list1: The first listto compare. | list2: The second listto compare. | msg: Optional messageto use on failure instead of a list of | differences. | | assertMultiLineEqual(self, first, second, msg=None) | Assert that two multi-line strings are equal. | | assertNotAlmostEqual(self, first, second, places=None, msg=None,delta=None) | Fail if the two objects are equal as determinedby their | difference rounded to the given number ofdecimal places | (default 7) and comparing to zero, or bycomparing that the | between the two objects is less than the givendelta. | | Note that decimal places (from zero) areusually not the same | as significant digits (measured from the mostsignficant digit). | | Objects that are equal automatically fail. | | assertNotAlmostEquals = assertNotAlmostEqual(self, first, second, places=None,msg=None, delta=None) | | assertNotEqual(self, first, second, msg=None) | Fail if the two objects are equal as determinedby the '!=' | operator. | | assertNotEquals = assertNotEqual(self, first, second, msg=None) | | assertNotIn(self, member, container, msg=None) | Just like self.assertTrue(a not in b), but witha nicer default message. | | assertNotIsInstance(self, obj, cls, msg=None) | Included for symmetry with assertIsInstance. | | assertNotRegexpMatches(self, text, unexpected_regexp, msg=None) | Fail the test if the text matches the regularexpression. | | assertRaises(self, excClass, callableObj=None, *args, **kwargs) | Fail unless an exception of class excClass israised | by callableObj when invoked with arguments argsand keyword | arguments kwargs. If a different type ofexception is | raised, it will not be caught, and the testcase will be | deemed to have suffered an error, exactly asfor an | unexpected exception. | | If called with callableObj omitted or None,will return a | context object used like this:: | | withself.assertRaises(SomeException): | do_something() | | The context manager keeps a reference to theexception as | the 'exception' attribute. This allows you toinspect the | exception after the assertion:: | | withself.assertRaises(SomeException) as cm: | do_something() | the_exception =cm.exception | self.assertEqual(the_exception.error_code, 3) | | assertRaisesRegexp(self, expected_exception, expected_regexp,callable_obj=None, *args, **kwargs) | Asserts that the message in a raised exceptionmatches a regexp. | | Args: | expected_exception:Exception class expected to be raised. | expected_regexp: Regexp(re pattern object or string) expected | to be found in error message. | callable_obj: Functionto be called. | args: Extra args. | kwargs: Extra kwargs. | | assertRegexpMatches(self, text, expected_regexp, msg=None) | Fail the test unless the text matches theregular expression. | | assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None) | An equality assertion for ordered sequences(like lists and tuples). | | For the purposes of this function, a validordered sequence type is one one | which can be indexed, has a length, and has anequality operator. | | Args: | seq1: The firstsequence to compare. | seq2: The secondsequence to compare. | seq_type: The expecteddatatype of the sequences, or None if no | datatype should be enforced. | msg: Optional messageto use on failure instead of a list of | differences. | | assertSetEqual(self, set1, set2, msg=None) | A set-specific equality assertion. | | Args: | set1: The first set tocompare. | set2: The second set tocompare. | msg: Optional messageto use on failure instead of a list of | differences. | | assertSetEqual uses ducktyping to supportdifferent types of sets, and | is optimized for sets specifically (parametersmust support a | difference method). | | assertTrue(self, expr, msg=None) | Check that the expression is true. | | assertTupleEqual(self, tuple1, tuple2, msg=None) | A tuple-specific equality assertion. | | Args: | tuple1: The first tupleto compare. | tuple2: The secondtuple to compare. | msg: Optional messageto use on failure instead of a list of | differences.
前言
到unittest这里基本上能够搭建一个简易的项目框架了,咱们能够用一条run_main.py脚本去控制执行全部的用例,并生成报告,发送邮件一系列的动做
1、新建工程
1.打开pycharm左上角File>New Project,在Location位置输入项目名称:D:\test\test_blog
2.建立以后,选择Opin in current window就能够了
2、项目结构
1.在测试工程下,建立文件夹,必定要选Python Package的方式建立,要否则后面导入本身写的模块会出现各类问题
2.在工程下建立如下几个文件
--test_case 这个文件夹放全部测试用例
----blog_home 能够按功能用例模块划分
---------test_home
---------test_home_1 测试用例以test开头命名
----blog_login
---------test_login
----blog_set
---------test_set
--test_report
--run_main.py 注意这个脚本放文件根目录
3、run_main
1.run_main.py这个脚本里面写主函数,控制执行全部的用例,最终咱们只须要运行这个脚本就能够了
2.咱们也能够在cmd里执行这个脚本文件,这样就不用依赖pycharm去执行了(后续用jenkins执行,也是一样道理,启动cmd执行脚本)
>>d:
>>cd test\test_blog
>>python run_main.py
3.run_main.py源代码在下一章节。
生成测试项目报告模板:
https://files.cnblogs.com/files/zidonghua/%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF.zip
测试报告搞不出来的看这里:
https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C2.rar
如下代码在python2和python3上都跑经过,python3只需注释掉上面红色框框区域代码就行(最后一步发送邮箱代码,我注释掉了)。
# coding=utf-8 import unittest import time import HTMLTestRunner from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import smtplib import os ####下面三行代码python2报告出现乱码时候能够加上#### import sys reload(sys) sys.setdefaultencoding('utf8')
# 这个是优化版执行全部用例并发送报告,分四个步骤 # 第一步加载用例 # 第二步执行用例 # 第三步获取最新测试报告 # 第四步发送邮箱 (这一步不想执行的话,能够注释掉最后面那个函数就行)
def add_case(case_path, rule): '''加载全部的测试用例''' testunit = unittest.TestSuite() # 定义discover方法的参数 discover = unittest.defaultTestLoader.discover(case_path, pattern=rule, top_level_dir=None) # discover方法筛选出来的用例,循环添加到测试套件中 # for test_suite in discover: # for test_case in test_suite: # testunit.addTests(test_case) # print testunit testunit.addTests(discover) # 直接加载discover print(testunit) return testunit
def run_case(all_case, report_path): '''执行全部的用例, 并把结果写入测试报告''' now = time.strftime("%Y_%m_%d %H_%M_%S") report_abspath = os.path.join(report_path, now+"result.html") # report_abspath = "D:\\web_project\\report\\"+now+"result.html" fp = open(report_abspath, "wb") runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'自动化测试报告,测试结果以下:', description=u'用例执行状况:') # 调用add_case函数返回值 runner.run(all_case) fp.close()
def get_report_file(report_path): '''获取最新的测试报告''' lists = os.listdir(report_path) lists.sort(key=lambda fn: os.path.getmtime(os.path.join(report_path, fn))) print (u'最新测试生成的报告: '+lists[-1]) # 找到最新生成的报告文件 report_file = os.path.join(report_path, lists[-1]) return report_file
def send_mail(sender, psw, receiver, smtpserver, report_file): '''发送最新的测试报告内容''' # 读取测试报告的内容 with open(report_file, "rb") as f: mail_body = f.read() # 定义邮件内容 msg = MIMEMultipart() body = MIMEText(mail_body, _subtype='html', _charset='utf-8') msg['Subject'] = u"自动化测试报告" msg["from"] = sender msg["to"] = psw # 加上时间戳 # msg["date"] = time.strftime('%a, %d %b %Y %H_%M_%S %z') msg.attach(body) # 添加附件 att = MIMEText(open(report_file, "rb").read(), "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename= "report.html"' msg.attach(att) # 登陆邮箱 smtp = smtplib.SMTP() # 链接邮箱服务器 smtp.connect(smtpserver) # 用户名密码 smtp.login(sender, psw) smtp.sendmail(sender, receiver, msg.as_string()) smtp.quit() print('test report email has send out !')if __name__ == "__main__": # 测试用例的路径、匹配规则 case_path = "D:\\test\\newp\\case" rule = "test*.py" all_case = add_case(case_path, rule) # 1加载用例 # 生成测试报告的路径 report_path = "D:\\test\\newp\\report" run_case(all_case, report_path) # 2执行用例 # 获取最新的测试报告文件 report_file = get_report_file(report_path) # 3获取最新的测试报告 #邮箱配置 sender = "yoyo@xxx.com" psw = "xxx" # 收件人多个时,中间用逗号隔开,如'a@xx.com,b@xx.com' receiver = "yoyo@xxx.com" smtp_server = 'smtp.xxx.com' # send_mail(sender, psw, receiver, smtp_server, report_file) # 4最后一步发送报告,须要发邮件就取消注释。
以登陆博客园为案例https://passport.cnblogs.com/user/signin
1、登陆方法封装
1.咱们能够把登陆写成一个登陆类,里面写个登陆的方法,保存文件为login_pub.py
# coding:utf-8 ''' 这里写了一个登陆博客园的类,登陆博客园方法 ''' class Login_Blog(): '''登陆类封装''' def __init__(self, driver): '''初始化driver参数''' self.driver = driver def input_user(self, username): '''输入用户名''' self.driver.find_element_by_id("input1").clear() self.driver.find_element_by_id("input1").send_keys(username) def input_psw(self,psw): '''输入密码''' self.driver.find_element_by_id("input2").clear() self.driver.find_element_by_id("input2").send_keys(psw) def click_button(self): '''点击登陆按钮''' self.driver.find_element_by_id("signin").click() def login(self, username, psw): '''登陆公共方法''' self.input_user(username) self.input_psw(psw) self.click_button()
2.调用登陆公共方法
# coding:utf-8 from selenium import webdriver import unittest from login_pub import Login_Blog login_url = "https://passport.cnblogs.com/user/signin"
class TetsLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.get(login_url) def tearDown(self): self.driver.quit() def test_login(self): # 调用登陆类里面的login方法 Login_Blog(self.driver).login("xxx", "111") self.driver.find_element() # 后面接着的操做省略了
if __name__ == "__main__": unittest.main()
前言
在定位元素的时候,常常会遇到各类异常,为何会发生这些异常,遇到异常又该如何处理呢?
本篇经过学习selenium的exceptions模块,了解异常发生的缘由。
1、发生异常
1.打开博客首页,定位“新随笔”元素,此元素
2.为了故意让它定位失败,我在元素属性后面加上xx
3.运行失败后以下图所示,程序在查找元素的这一行发生了中断,不会继续执行click事件了
2、捕获异常
1.为了让程序继续执行,咱们能够用try...except...捕获异常。捕获异常后能够打印出异常缘由,这样以便于分析异常缘由。
2.从以下异常内容能够看出,发生异常缘由是:NoSuchElementException
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: {"method":"id","selector":"blog_nav_newpostxx"}
3.从selenium.common.exceptions 导入 NoSuchElementException类。
3、参考代码:
# coding:utf-8 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Firefox() driver.get("http://www.cnblogs.com/yoyoketang/")
# 定位首页"新随笔" try: element = driver.find_element("id", "blog_nav_newpostxx") except NoSuchElementException as msg: print u"查找元素异常%s"%msg # 点击该元素 else: element.click()
4、selenium常见异常
1.NoSuchElementException:没有找到元素
2.NoSuchFrameException:没有找到iframe
3.NoSuchWindowException:没找到窗口句柄handle
4.NoSuchAttributeException:属性错误
5.NoAlertPresentException:没找到alert弹出框
6.ElmentNotVisibleException:元素不可见
7.ElementNotSelectableException:元素没有被选中
8.TimeoutException:查找元素超时
备注:其它异常与源码在Lib目录下:selenium/common/exceptions有兴趣的能够看看。
前言
在执行用例过程当中因为是无人值守的,用例运行报错的时候,咱们但愿能对当前屏幕截图,留下证据。
在写用例的时候,最后一步是断言,能够把截图的动做放在断言这里,那么如何在断言失败后截图呢?
1、截图方法
1.get_screenshot_as_file(self, filename)
--这个方法是获取当前window的截图,出现IOError时候返回False,截图成功返回True。
filename参数是保存文件的路径。
Usage:
driver.get_screenshot_as_file('/Screenshots/foo.png')
2.get_screenshot_as_base64(self)
--这个方法也是获取屏幕截图,保存的是base64的编码格式,在HTML界面输出截图的时候,会用到。
好比,想把截图放到html测试报告里。
Usage:
driver.get_screenshot_as_base64()
3.get_screenshot_as_png(self)
--这个是获取屏幕截图,保存的是二进制数据,不多用到。
Usage:
driver.get_screenshot_as_png()
2、异常后截图
1.为了能抛异常,把定位登陆按钮的id换了个错的id。
2.给图片命名时候加个时间戳,避免同一个文件名称被覆盖掉。
3.文件路径,这里直接写的文件名称,就是跟当前的脚本同一个路径。若是图片输出到其它文件路径,须要些文件的绝对路径了。
4.截图的结果,若是没截到图返回False,截图成功会返回True。
3、selenium实例
1.在unittest框架里写用例的时候,咱们但愿在断言失败的时候,对当前屏幕截图。
2.若是加try...except捕获异常后结果,此时全部的测试用例都是经过的了,会影响测试结果。解决办法其实很简单,再把异常抛出来就好了。
3.参考代码:
# coding:utf-8 from selenium import webdriver import time,unittest from selenium.webdriver.support import expected_conditions as EC
class Login(unittest.TestCase): def setUp(self): url_login = "https://passport.cnblogs.com/user/signin" self.driver = webdriver.Firefox() self.driver.get(url_login) def test_01(self): '''前面输入帐号密码,让正确运行到assert这一步,断言故意设置为Fals e不成功''' try: self.driver.find_element_by_id("input1").send_keys(u"上海-悠悠") self.driver.find_element_by_id("input2").send_keys("xxx") # 登陆id是错的,定位会抛异常 self.driver.find_element_by_id("signin").click() # 判断登陆成功页面是否有帐号:"上海-悠悠" time.sleep(3) locator = ("id", "lnk_current_user") result = EC.text_to_be_present_in_element(locator,u"上海-悠悠")(self.driver) self.assertFalse(result) except Exception as msg: print(u"异常缘由%s"%msg) # 图片名称能够加个时间戳 nowTime = time.strftime("%Y%m%d.%H.%M.%S") self.driver.get_screenshot_as_file('%s.jpg' % nowTime) raise def tearDown(self): self.driver.quit()
if __name__ == "__main__": unittest.main()
4.运行结果:
异常缘由True is not false
Failure
Traceback (most recent call last):
File "D:\test\yoyot\ketang\test01.py", line 22, in test_01
self.assertFalse(result)
AssertionError: True is not false
前言
本篇总结了QQ邮箱和163邮箱发送邮件,邮件包含html中文和附件,能够发给多个收件人,专治各类不行,总之看完这篇麻麻不再用担忧个人邮件收不到了。
如下代码兼容python2和python3,运行无异常,放心大胆食用。
1、163邮箱
1.先导入smtplib库用来发送邮件,导入MIMEText库用来作纯文本的邮件模板
2.先准备几个跟发邮件相关的参数,每一个邮箱的发件服务器都不同,以163为例,百度搜到发件服务器为:smtp.163.com
3.接下来就是写邮件的主题和正文内容,正文这里用html格式的
4.最后调用SMTP发件服务
5.参考代码:
# coding:utf-8 import smtplib from email.mime.text import MIMEText
# ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 帐号 psw = "**************" # 密码 receiver = "283340479@qq.com" # 接收人
# ----------2.编辑邮件的内容------ subject = "这个是主题163" body = '<p>这个是发送的163邮件</p>' # 定义邮件正文为html格式 msg = MIMEText(body, "html", "utf-8") msg['from'] = sender msg['to'] = "283340479@qq.com" msg['subject'] = subject
# ----------3.发送邮件------ smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) # 登陆 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭
2、QQ邮件
1.QQ邮箱是须要SSL认证的,这种邮箱跟上面的就有点不同了。
2.找到QQ邮箱受权码,打开QQ邮箱-设置-帐号-POP3开启服务-开启
(若是已经开启了,不知道受权码,就点舒适提示里面的‘生成受权码’)
3.发验证短信获取受权码,照着提示发个短信,如何点我已发送,就会收到受权码了。
4.收到受权码后复制,保存下来,这个就能够当QQ邮箱的密码了。
5.QQ邮箱发送邮件代码,跟163有点不同,以下图红色框框:
6.参考代码:
# coding:utf-8 import smtplib from email.mime.text import MIMEText
# ----------1.跟发件相关的参数------ # smtpserver = "smtp.163.com" # 发件服务器 smtpserver = "smtp.qq.com" port = 465 # 端口 sender = "283340479@qq.com" # 帐号 psw = "**************" # 密码 receiver = "283340479@qq.com" # 接收人
# ----------2.编辑邮件的内容------ subject = "这个是主题QQ" body = '<p>这个是发送的QQ邮件</p>' # 定义邮件正文为html格式 msg = MIMEText(body, "html", "utf-8") msg['from'] = sender msg['to'] = "283340479@qq.com" msg['subject'] = subject
# ----------3.发送邮件------ # smtp = smtplib.SMTP() # smtp.connect(smtpserver) # 连服务器 smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登陆 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭
3、兼容163和QQ邮箱
1.若是想兼容上面两种方式发送邮件,只需把第三块内容稍微改下,以下所示
4、发送带附件
1.上面的MIMEText只能发送正文,没法带附件,发送带附件的须要导入另一个模块MIMEMultipart
2.先读取要发送文件的内容,file_path是路径的参数名
3.下图红色框框file_name参数是发送的附件从新命名
4.参考代码:
# coding:utf-8 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart
# ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 帐号 psw = "***********" # 密码 receiver = "283340479@qq.com" # 接收人
# ----------2.编辑邮件的内容------ # 读文件 file_path = "result.html" with open(file_path, "rb") as fp: mail_body = fp.read() msg = MIMEMultipart() msg["from"] = sender # 发件人 msg["to"] = receiver # 收件人 msg["subject"] = "这个个人主题" # 主题 # 正文 body = MIMEText(mail_body, "html", "utf-8") msg.attach(body) # 附件 att = MIMEText(mail_body, "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename="test_report.html"' msg.attach(att)
# ----------3.发送邮件------ try: smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) except: smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登陆 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit()
5.最后结果,有图有真相
5、发给多个收件人
1.上面都是发给一个收件人,那么如何一次发给多个收件人呢?只需改两个小地方
2.把receiver参数改为list对象,单个多个都是能够收到的
3.msg["to"]这个参数不能用list了,得先把receiver参数转化成字符串,以下图所示
4.参考代码:
# coding:utf-8 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart
# ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 帐号 psw = "*********" # 密码 # receiver = ["xxxx@qq.com"] # 单个接收人也能够是list receiver = ["xxxx@qq.com", "yoyo@qq.com"] # 多个收件人list对象
# ----------2.编辑邮件的内容------ # 读文件 file_path = "result.html" with open(file_path, "rb") as fp: mail_body = fp.read() msg = MIMEMultipart() msg["from"] = sender # 发件人 msg["to"] = ";".join(receiver) # 多个收件人list转str msg["subject"] = "这个个人主题999" # 主题 # 正文 body = MIMEText(mail_body, "html", "utf-8") msg.attach(body) # 附件 att = MIMEText(mail_body, "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename="test_report.html"' msg.attach(att)
# ----------3.发送邮件------ try: smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) except: smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登陆 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭
六:邮件收不到的几种缘由:
1.Subject和正文内容不要用hello、hehe、test等单词
2.from(发件人)和to(收件人)不要为空,
(要否则会被认为是垃圾邮件)
3.找不到的话,先看下垃圾信箱,是否是跑到垃圾箱了
4.若是前几回能够收到,后来收不到了,需改下subject内容
(由于每次都是一个subject,系统也会拒收的,把subject内容设置为动态的是最好的)
5.部分邮箱是ssl加密了的,因此没法发送,如:qq邮箱
(用受权码去登陆)
6.要是按照上面的步骤来报错了,说明代码抄错了,多检查几回。
(以上代码均在python2和python3上都测试经过了)
前言
当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候咱们但愿暂时跳过这些用例。
或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,若是第一步就失败了,后面的用例也就不必去执行了,直接跳过就行,节省用例执行时间。
1、skip装饰器
skip装饰器一共有四个:
@unittest.skip(reason)
Unconditionally skip the decorated test. reason should describe why the test is being skipped.
翻译:无条件跳过用例,reason是说明缘由。
@unittest.skipIf(condition, reason)
Skip the decorated test if condition is true.
翻译:condition为true的时候跳过。
@unittest.skipUnless(condition, reason)
Skip the decorated test unless condition is true.
翻译:condition为False的时候跳过。
@unittest.expectedFailure
Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure.
翻译:断言的时候跳过。
2、skip案例
运行结果:
测试1
测试4
.ssx
----------------------------------------------------------------------
Ran 4 tests in 0.003s
OK (skipped=2, expected failures=1)
3、跳过整个测试类
4、参考代码:
# coding:utf-8 import unittest class Test(unittest.TestCase): @unittest.skip(u"无条件跳过此用例") def test_1(self): print "测试1" @unittest.skipIf(True, u"为True的时候跳过") def test_2(self): print "测试2" @unittest.skipUnless(False, u"为False的时候跳过") def test_3(self): print "测试3" @unittest.expectedFailure def test_4(self): print "测试4" self.assertEqual(2, 4, msg=u"判断相等") if __name__ == "__main__": unittest.main()