前言
(python基礎比較弱的,建議你們多花點時間把基礎語法學好,這裏有套視頻,能夠照着練習下:http://pan.baidu.com/s/1i44jZdb 密碼:92fs)
熟悉java的應該都清楚常見的單元測試框架Junit和TestNG,這個招聘的需求上也是常常見到的。python裏面也有單元測試框架-unittest,至關因而一個python版的junit。
python裏面的單元測試框架除了unittest,還有一個pytest框架,這個用的比較少,後面有空再繼續分享。html
1).先導入unittestjava
2).用help函數查看源碼解析
3).查看描述:
Python unit testing framework, based on Erich Gamma's JUnit and KentBeck's Smalltalk testing framework.
翻譯:python的單元測試框架,是基於java的junit測試框架。python
1).能夠把上圖的這段代碼copy出來,單獨運行,看看測試結果。git
Simple usage:web
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-判斷兩個是否相等,express
這個斷言能夠是一個也能夠是多個
7).if下面的這個unittest.main()是運行主函數,運行後會看到測試結果(跑了兩個用例耗時0.000秒,兩個用例都經過):
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OKpython3.x
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()
上述代碼運行的順序就是從上至下,而再也不是每次執行完成一個testcase以後,執行一次teardownClass再進行下一個testcase。
這樣一來,退出瀏覽器僅僅執行一次便可,這樣有一個很差的地方就是,teardownClass這個函數不能再進行每一個測試用例的終結操做,好比:修改我的信息後恢復到登陸成功後的狀態,對當前測試用例的異常處理等。
後來,本人嘗試在tearDownClass後增長以下代碼:
def tearDown(self): self.driver.refresh() self.assertEqual( [], self.verificationErrors )
而後,果真每次測試用完成都會刷新當前頁面,這樣一來,每個testcase的用例都能被終結函數tearDown結束,最後再執行tearDownClass關閉測試瀏覽器。
須要說明的是:
@classmethod是python自己的裝飾器,因此他不要使用隸屬於unittest框架下斷言assertEqual。
unittest自己也帶有裝飾器unittest.skip(),專門用於跳過testcase的裝飾器,其用法以下:
@unittest.skip(reason), skip裝飾器:直接跳過裝飾下的testcase,reason用來講明緣由,下同。
@unittest.skipIf(condition,reason), skipIf裝飾器:condition條件爲True時,跳過裝飾下的testcase,計入skip的testcase執行次數。
@unittest.skipUnless(condition,reason),skipUnless裝飾器:condition條件爲False時,跳過裝飾下的testcase,計入skip的testcase執行次數。
@unittest.expectedFailure(), expectedFailure裝飾器:執行裝飾下的testcase,執行失敗則跳過該testcase,計入expected下成敗的testcase次數。
通常來說,使用@unittest.skipIf 或者 @unittest.skipUnless,應該也能實現@classmethod裝飾器的效果, 想來只是實現起來相對來講較爲麻煩。
前言
批量執行完用例後,生成的測試報告是文本形式的,不夠直觀,爲了更好的展現測試報告,最好是生成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源代碼在下一章節。
生成測試項目報告模板:
測試報告搞不出來的看這裏:
如下代碼在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.打開博客首頁,定位「新隨筆」元素,此元素id="blog_nav_newpost"
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()