一、Pyhon工作原理—— 核心概念:test case, testsuite, TestLoder,TextTestRunner,TextTestResult, test fixture
TestCase(測試用例): 所有測試用例的基類,它是軟件 測試中最基本的組成單元。
一個test case就是一個測試用例,是一個完整的測試流程,包括測試前環境的搭建setUp,執行測試代碼(run),以及測試后環境的還原(tearDown)。測試用例是一個完整的測試單元,可以對某一問題進行驗證。
TestSuite(測試套件):多個測試用例test case集合就是TestSuite,TestSuite可以嵌套TestSuite
TestLoder:是用來加載 TestCase到TestSuite中,其中有幾個loadTestsFrom_()方法,就是從各個地方尋找TestCase,創建他們的實例,然后add到TestSuite中,再返回一個TestSuite實例
TextTestRunner:是來執行測試用例的,其中的run(test)會執行TestSuite/TestCase中的run(result)方法。
TextTestResult:測試結果會保存到TextTestResult實例中,包括運行了多少用例,成功與失敗多少等信息
TestFixture:又叫測試腳手,測試代碼的運行環境,指測試准備前和執行后要做的工作,包括setUp和tearDown方法
二、測試流程:
1. 寫好TestCase:一個class繼承unittest.TestCase,就是一個測試測試用例,其中有多個以test開頭的方法,那么 每一個這樣的,在load的時候會生成一個TestCase實例。如果一個class中有四個test開頭的方法,最后load到suite中時則有四個測試用例
2. 由TestLoder加載TestCase到TestSuite
3.然后由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中。
說明:a:通過命令行或者unittest.main()執行時,main會調用TextTestRunner中的run來執行,或者可以直接通過TextTestRunner來執行用例
b:Runner執行時,默認將結果輸出到控制台,我們可以設置其輸出到文件,在文件中查看 結果,也可以通過HTMLTestRunner將結果輸出到HTML)
setUp()
和 tearDown()
兩個方法(其實是重寫了TestCase的這兩個方法),這兩個方法在每個測試方法執行前以及執行后執行一次,setUp用來為測試准備環境,tearDown用來清理環境,已備之后的測試。
- unittest是Python自帶的單元測試框架,我們可以用其來作為我們自動化測試框架的用例組織執行框架。
- unittest的流程:寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,我們通過命令行或者unittest.main()執行時,main會調用TextTestRunner中的run來執行,或者我們可以直接通過TextTestRunner來執行用例。
- 一個class繼承unittest.TestCase即是一個TestCase,其中以
test
開頭的方法在load時被加載為一個真正的TestCase。 - verbosity參數可以控制執行結果的輸出,
0
是簡單報告、1
是一般報告、2
是詳細報告。 - 可以通過addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。
- 用
setUp()
、tearDown()
、setUpClass()
以及tearDownClass()
可以在用例執行前布置環境,以及在用例執行后清理環境 - 我們可以通過skip,skipIf,skipUnless裝飾器跳過某個case,或者用TestCase.skipTest方法。
- 參數中加stream,可以將報告輸出到文件:可以用TextTestRunner輸出txt報告,以及可以用HTMLTestRunner輸出html報告。
一、unittest模塊的各個屬性說明
先來聊一聊unittest模塊的各個屬性,所謂知己知彼方能百戰百勝,了解unittest的各個屬性,對於后續編寫用例有很大的幫助。
1.unittest的屬性如下:
['BaseTestSuite', 'FunctionTestCase', 'SkipTest', 'TestCase', 'TestLoader', 'TestProgram', 'TestResult', 'TestSuite', 'TextTestResult', 'TextTestRunner', '_TextTestResult', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__unittest', 'case', 'defaultTestLoader', 'expectedFailure', 'findTestCases', 'getTestCaseNames', 'installHandler', 'loader', 'main', 'makeSuite', 'registerResult', 'removeHandler', 'removeResult', 'result', 'runner', 'signals', 'skip', 'skipIf', 'skipUnless', 'suite', 'util']
說明:
unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。
class BaiduTest(unittest.TestCase):
unittest.main():使用她可以方便的將一個單元測試模塊變為可直接運行的測試腳本,main()方法使用TestLoader類來搜索所有包含在該模塊中以“test”命名開頭的測試方法,並自動執行他們。執行方法的默認順序是:根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以以A開頭的測試用例方法會優先執行,以a開頭會后執行。
unittest.TestSuite():unittest框架的TestSuite()類是用來創建測試套件的。
unittest.TextTextRunner():unittest框架的TextTextRunner()類,通過該類下面的run()方法來運行suite所組裝的測試用例,入參為suite測試套件。
unittest.defaultTestLoader(): defaultTestLoader()類,通過該類下面的discover()方法可自動更具測試目錄start_dir匹配查找測試用例文件(test*.py),並將查找到的測試用例組裝到測試套件,因此可以直接通過run()方法執行discover。用法如下:
discover=unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
unittest.skip():裝飾器,當運行用例時,有些用例可能不想執行等,可用裝飾器暫時屏蔽該條測試用例。一種常見的用法就是比如說想調試某一個測試用例,想先屏蔽其他用例就可以用裝飾器屏蔽。
@unittest.skip(reason): skip(reason)裝飾器:無條件跳過裝飾的測試,並說明跳過測試的原因。
@unittest.skipIf(reason): skipIf(condition,reason)裝飾器:條件為真時,跳過裝飾的測試,並說明跳過測試的原因。
@unittest.skipUnless(reason): skipUnless(condition,reason)裝飾器:條件為假時,跳過裝飾的測試,並說明跳過測試的原因。
@unittest.expectedFailure(): expectedFailure()測試標記為失敗。
2.TestCase類的屬性如下:
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_addSkip', '_baseAssertEqual', '_classSetupFailed', '_deprecate', '_diffThreshold', '_formatMessage', '_getAssertEqualityFunc', '_truncateMessage', 'addCleanup', 'addTypeEqualityFunc', 'assertAlmostEqual', 'assertAlmostEquals', 'assertDictContainsSubset', 'assertDictEqual', 'assertEqual', 'assertEquals', 'assertFalse', 'assertGreater', 'assertGreaterEqual', 'assertIn', 'assertIs', 'assertIsInstance', 'assertIsNone', 'assertIsNot', 'assertIsNotNone', 'assertItemsEqual', 'assertLess', 'assertLessEqual', 'assertListEqual', 'assertMultiLineEqual', 'assertNotAlmostEqual', 'assertNotAlmostEquals', 'assertNotEqual', 'assertNotEquals', 'assertNotIn', 'assertNotIsInstance', 'assertNotRegexpMatches', 'assertRaises', 'assertRaisesRegexp', 'assertRegexpMatches', 'assertSequenceEqual', 'assertSetEqual', 'assertTrue', 'assertTupleEqual', 'assert_', 'countTestCases', 'debug', 'defaultTestResult', 'doCleanups', 'fail', 'failIf', 'failIfAlmostEqual', 'failIfEqual', 'failUnless', 'failUnlessAlmostEqual', 'failUnlessEqual', 'failUnlessRaises', 'failureException', 'id', 'longMessage', 'maxDiff', 'run', 'setUp', 'setUpClass', 'shortDescription', 'skipTest', 'tearDown', 'tearDownClass']
說明:
setUp():setUp()方法用於測試用例執行前的初始化工作。如測試用例中需要訪問數據庫,可以在setUp中建立數據庫連接並進行初始化。如測試用例需要登錄web,可以先實例化瀏覽器。
tearDown():tearDown()方法用於測試用例執行之后的善后工作。如關閉數據庫連接。關閉瀏覽器。
assert*():一些斷言方法:在執行測試用例的過程中,最終用例是否執行通過,是通過判斷測試得到的實際結果和預期結果是否相等決定的。
assertEqual(a,b,[msg='測試失敗時打印的信息']):斷言a和b是否相等,相等則測試用例通過。
assertNotEqual(a,b,[msg='測試失敗時打印的信息']):斷言a和b是否相等,不相等則測試用例通過。
assertTrue(x,[msg='測試失敗時打印的信息']):斷言x是否True,是True則測試用例通過。
assertFalse(x,[msg='測試失敗時打印的信息']):斷言x是否False,是False則測試用例通過。
assertIs(a,b,[msg='測試失敗時打印的信息']):斷言a是否是b,是則測試用例通過。
assertNotIs(a,b,[msg='測試失敗時打印的信息']):斷言a是否是b,不是則測試用例通過。
assertIsNone(x,[msg='測試失敗時打印的信息']):斷言x是否None,是None則測試用例通過。
assertIsNotNone(x,[msg='測試失敗時打印的信息']):斷言x是否None,不是None則測試用例通過。
assertIn(a,b,[msg='測試失敗時打印的信息']):斷言a是否在b中,在b中則測試用例通過。
assertNotIn(a,b,[msg='測試失敗時打印的信息']):斷言a是否在b中,不在b中則測試用例通過。
assertIsInstance(a,b,[msg='測試失敗時打印的信息']):斷言a是是b的一個實例,是則測試用例通過。
assertNotIsInstance(a,b,[msg='測試失敗時打印的信息']):斷言a是是b的一個實例,不是則測試用例通過。
3.TestSuite類的屬性如下:(組織用例時需要用到)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_addClassOrModuleLevelException', '_get_previous_module', '_handleClassSetUp', '_handleModuleFixture', '_handleModuleTearDown', '_tearDownPreviousClass', '_tests', 'addTest', 'addTests', 'countTestCases', 'debug', 'run']
說明:
addTest(): addTest()方法是將測試用例添加到測試套件中,如下方,是將test_baidu模塊下的BaiduTest類下的test_baidu測試用例添加到測試套件。
suite = unittest.TestSuite() suite.addTest(test_baidu.BaiduTest('test_baidu'))
4.TextTextRunner的屬性如下:(組織用例時需要用到)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_makeResult', 'buffer', 'descriptions', 'failfast', 'resultclass', 'run', 'stream', 'verbosity']
說明:
run(): run()方法是運行測試套件的測試用例,入參為suite測試套件。
runner = unittest.TextTestRunner() runner.run(suite)
設計基本思路如下:
# coding=utf-8 #1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行 #2.注釋:包括記錄創建時間,創建人,項目名稱。 ''' Created on 2016-7-27 @author: Jennifer Project:使用unittest框架編寫測試用例思路 ''' #3.導入unittest模塊 import unittest #4.定義測試類,父類為unittest.TestCase。 #可繼承unittest.TestCase的方法,如setUp和tearDown方法,不過此方法可以在子類重寫,覆蓋父類方法。 #可繼承unittest.TestCase的各種斷言方法。 class Test(unittest.TestCase): #5.定義setUp()方法用於測試用例執行前的初始化工作。 #注意,所有類中方法的入參為self,定義方法的變量也要“self.變量” #注意,輸入的值為字符型的需要轉為int型 def setUp(self): self.number=raw_input('Enter a number:') self.number=int(self.number) #6.定義測試用例,以“test_”開頭命名的方法 #注意,方法的入參為self #可使用unittest.TestCase類下面的各種斷言方法用於對測試結果的判斷 #可定義多個測試用例 #最重要的就是該部分 def test_case1(self): print self.number self.assertEqual(self.number,10,msg='Your input is not 10') def test_case2(self): print self.number self.assertEqual(self.number,20,msg='Your input is not 20') @unittest.skip('暫時跳過用例3的測試') def test_case3(self): print self.number self.assertEqual(self.number,30,msg='Your input is not 30') #7.定義tearDown()方法用於測試用例執行之后的善后工作。 #注意,方法的入參為self def tearDown(self): print 'Test over' #8如果直接運行該文件(__name__值為__main__),則執行以下語句,常用於測試腳本是否能夠正常運行 if __name__=='__main__': #8.1執行測試用例方案一如下: #unittest.main()方法會搜索該模塊下所有以test開頭的測試用例方法,並自動執行它們。 #執行順序是命名順序:先執行test_case1,再執行test_case2 unittest.main() ''' #8.2執行測試用例方案二如下: #8.2.1先構造測試集 #8.2.1.1實例化測試套件 suite=unittest.TestSuite() #8.2.1.2將測試用例加載到測試套件中。 #執行順序是安裝加載順序:先執行test_case2,再執行test_case1 suite.addTest(Test('test_case2')) suite.addTest(Test('test_case1')) #8.2.2執行測試用例 #8.2.2.1實例化TextTestRunner類 runner=unittest.TextTestRunner() #8.2.2.2使用run()方法運行測試套件(即運行測試套件中的所有用例) runner.run(suite)
''' ''' #8.3執行測試用例方案三如下: #8.3.1構造測試集(簡化了方案二中先要創建測試套件然后再依次加載測試用例) #執行順序同方案一:執行順序是命名順序:先執行test_case1,再執行test_case2 test_dir = './' discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py') #8.3.2執行測試用例 #8.3.2.1實例化TextTestRunner類 runner=unittest.TextTestRunner() #8.3.2.2使用run()方法運行測試套件(即運行測試套件中的所有用例) runner.run(discover) '''
使用方案一執行測試用例結果如下:
Enter a number:10
10
Test over
Enter a number:.10
Fs
Ran 3 tests in 6.092s
FAILED (failures=1, skipped=1)
10
Test over
因為先執行test_case1,再執行test_case2,所以第一次輸入10時,執行通過,返回. 第二次輸入10時,執行不通過,返回F,最終一個用例通過,一個用例失敗,還有一個用例是直接跳過的(裝飾器)。
使用方案二執行測試用例結果如下:
Enter a number:10
10
Test over
Enter a number:F10
.
Ran 2 tests in 4.973s
FAILED (failures=1)
10
Test over
因為先執行test_case2,再執行test_case1,所以第一次輸入10時,執行不通過,返回F , 第二次輸入10時,執行通過,返回. ,最終一個用例通過,一個用例失敗。
使用方案三執行測試用例結果如下(執行測試用例順序同方案一):
Enter a number:10
10
Test over
Enter a number:.10
Fs
Ran 3 tests in 6.092s
FAILED (failures=1, skipped=1)
10
Test over
因為先執行test_case1,再執行test_case2,所以第一次輸入10時,執行通過,返回. 第二次輸入10時,執行不通過,返回F,最終一個用例通過,一個用例失敗,還有一個用例是直接跳過的(裝飾器)。
目錄結構:
百度搜索測試用例Test Case:
# coding=utf-8 ''' Created on 2016-7-22 @author: Jennifer Project:登錄百度測試用例 ''' from selenium import webdriver import unittest, time class BaiduTest(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) #隱性等待時間為30秒 self.base_url = "https://www.baidu.com" def test_baidu(self): driver = self.driver driver.get(self.base_url + "/") driver.find_element_by_id("kw").clear() driver.find_element_by_id("kw").send_keys("unittest") driver.find_element_by_id("su").click() time.sleep(3) title=driver.title self.assertEqual(title, u"unittest_百度搜索") def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
有道翻譯測試用例Test Case:
# coding=utf-8 ''' Created on 2016-7-22 @author: Jennifer Project:使用有道翻譯測試用例 ''' from selenium import webdriver import unittest, time class YoudaoTest(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) #隱性等待時間為30秒 self.base_url = "http://www.youdao.com" def test_youdao(self): driver = self.driver driver.get(self.base_url + "/") driver.find_element_by_id("translateContent").clear() driver.find_element_by_id("translateContent").send_keys(u"你好") driver.find_element_by_id("translateContent").submit() time.sleep(3) page_source=driver.page_source self.assertIn( "hello",page_source) def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
web測試用例:通過測試套件TestSuite來組裝多個測試用例。
# coding=utf-8 ''' Created on 2016-7-26 @author: Jennifer Project:編寫Web測試用例 ''' import unittest from test_case import test_baidu from test_case import test_youdao #構造測試集 suite = unittest.TestSuite() suite.addTest(test_baidu.BaiduTest('test_baidu')) suite.addTest(test_youdao.YoudaoTest('test_youdao')) if __name__=='__main__': #執行測試 runner = unittest.TextTestRunner() runner.run(suite)
1) 桌面新建一個記事本,命名並保存為FirstScript.py
2) 輸入如下代碼片段到FirstScript.py,保存
3) 打開cmd窗口,切換到桌面路徑下,然后通過python FirstScript.py 來執行腳本。
FirstScript.py腳本如下
- # coding=utf-8
- import time
- from selenium import webdriver
- driver = webdriver.Chrome() # 打開chrome,如果沒有安裝chrome,換成webdriver.Firefox()
- driver.maximize_window() # 最大化瀏覽器窗口
- driver.implicitly_wait(8) # 設置隱式時間等待
- driver.get("https://www.baidu.com") # 地址欄輸入百度地址
- driver.find_element_by_xpath("//*[@id='kw']").send_keys("selenium") # 搜索輸入框輸入Selenium
- driver.find_element_by_xpath("//*[@id='su']").click() #點擊百度一下按鈕
- # 導入time模塊,等待2秒
- time.sleep(2)
- # 這里通過元素XPath表達式來確定該元素顯示在結果列表,從而判斷Selenium官網這個鏈接顯示在結果列表。
- # 這里采用了相對元素定位方法/../
- # 通過selenium方法is_displayed() 來判斷我們的目標元素是否在頁面顯示。
- driver.find_element_by_xpath("//div/h3/a[text()='官網']/../a/em[text()='Selenium']").is_displayed()
- driver.quit()
第二種斷言寫法:
- # coding=utf-8
- import time
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.maximize_window()
- driver.implicitly_wait(8)
- driver.get("https://www.baidu.com")
- driver.find_element_by_xpath("//*[@id='kw']").send_keys("selenium")
- driver.find_element_by_xpath("//*[@id='su']").click()
- time.sleep(2)
- # 第二個判斷方法
- ele_string = driver.find_element_by_xpath("//div/h3/a[text()='官網']/../a").text
- if (ele_string == u"Selenium - Web Browser Automation"):
- print "測試成功,結果和預期結果匹配!"
- driver.quit()
這里只利用 兩個等號(==)來判斷兩個字符串是否完全相同,有時候我們還需要對得到的字符串進行切割操作,才能進行去匹配,以后再介紹字符串切割處理在自動化測試結果判斷中的使用。
總結:自動化測試最難的在於如何去寫斷言,如何判斷測試結果是否通過。