selenium自动化测试、Python单元测试unittest框架以及测试报告和日志输出
部分內(nèi)容來(lái)自:https://www.cnblogs.com/klb561/p/8858122.html
一、基礎(chǔ)介紹
?核心概念:test case, testsuite, TestLoder,TextTestRunner,TextTestResult, test fixture
TestCase(測(cè)試用例):?所有測(cè)試用例的基類(lèi),它是軟件 測(cè)試中最基本的組成單元。
?一個(gè)test case就是一個(gè)測(cè)試用例,是一個(gè)完整的測(cè)試流程,包括測(cè)試前環(huán)境的搭建setUp,執(zhí)行測(cè)試代碼(run),以及測(cè)試后環(huán)境的還原(tearDown)。測(cè)試用例是一個(gè)完整的測(cè)試單元,可以對(duì)某一問(wèn)題進(jìn)行驗(yàn)證。
TestSuite(測(cè)試套件):多個(gè)測(cè)試用例test case集合就是TestSuite,TestSuite可以嵌套TestSuite
TestLoder:是用來(lái)加載 TestCase到TestSuite中,其中有幾個(gè)loadTestsFrom_()方法,就是從各個(gè)地方尋找TestCase,創(chuàng)建他們的實(shí)例,然后add到TestSuite中,再返回一個(gè)TestSuite實(shí)例
TextTestRunner:是來(lái)執(zhí)行測(cè)試用例的,其中的run(test)會(huì)執(zhí)行TestSuite/TestCase中的run(result)方法。
?TextTestResult:測(cè)試結(jié)果會(huì)保存到TextTestResult實(shí)例中,包括運(yùn)行了多少用例,成功與失敗多少等信息
TestFixture:又叫測(cè)試腳手,測(cè)試代碼的運(yùn)行環(huán)境,指測(cè)試準(zhǔn)備前和執(zhí)行后要做的工作,包括setUp和tearDown方法
二、測(cè)試流程
1)unittest是Python自帶的單元測(cè)試框架,我們可以用其來(lái)作為我們自動(dòng)化測(cè)試框架的用例組織執(zhí)行框架。
2)unittest的流程:寫(xiě)好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由 TextTestRunner來(lái)運(yùn)行TestSuite,運(yùn)行的結(jié)果保存在TextTestResult中,我們通過(guò)命令行或者unittest.main()執(zhí)行時(shí),main會(huì)調(diào)用TextTestRunner中的run來(lái)執(zhí)行,或者我們可以直接通過(guò) TextTestRunner來(lái)執(zhí)行用例。
3)一個(gè)class繼承unittest.TestCase即是一個(gè)TestCase,其中以?test?開(kāi)頭的方法在load時(shí)被加載為一個(gè)真正的TestCase。
4)verbosity參數(shù)可以控制執(zhí)行結(jié)果的輸出,0?是簡(jiǎn)單報(bào)告、1?是一般報(bào)告、2?是詳細(xì)報(bào)告。
5)可以通過(guò)addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。
6)用?setUp()、tearDown()、setUpClass()以及?tearDownClass()可以在用例執(zhí)行前布置環(huán)境,以及在用例執(zhí)行后清理環(huán)境
7)我們可以通過(guò)skip,skipIf,skipUnless裝飾器跳過(guò)某個(gè)case,或者用TestCase.skipTest方法。
8)參數(shù)中加stream,可以將報(bào)告輸出到文件:可以用TextTestRunner輸出txt報(bào)告,以及可以用HTMLTestRunner輸出html報(bào)告。
三、unittest模塊的各個(gè)屬性說(shuō)明
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']
說(shuō)明:
unittest.TestCase:TestCase類(lèi),所有測(cè)試用例類(lèi)繼承的基本類(lèi)。
class BaiduTest(unittest.TestCase):unittest.main():使用她可以方便的將一個(gè)單元測(cè)試模塊變?yōu)榭芍苯舆\(yùn)行的測(cè)試腳本,main() 方法使用TestLoader類(lèi)來(lái)搜索所有包含在該模塊中以“test”命名開(kāi)頭的測(cè)試方法,并自動(dòng)執(zhí)行他們。執(zhí)行方法的默認(rèn)順序是:根據(jù)ASCII碼的 順序加載測(cè)試用例,數(shù)字與字母的順序?yàn)?#xff1a;0-9,A-Z,a-z。所以以A開(kāi)頭的測(cè)試用例方法會(huì)優(yōu)先執(zhí)行,以a開(kāi)頭會(huì)后執(zhí)行。
unittest.TestSuite():unittest框架的TestSuite()類(lèi)是用來(lái)創(chuàng)建測(cè)試套件的。
unittest.TextTextRunner():unittest框架的TextTextRunner()類(lèi),通過(guò)該類(lèi)下面的run()方法來(lái)運(yùn)行suite所組裝的測(cè)試用例,入?yún)閟uite測(cè)試套件。
unittest.defaultTestLoader():?defaultTestLoader()類(lèi),通 過(guò)該類(lèi)下面的discover()方法可自動(dòng)更具測(cè)試目錄start_dir匹配查找測(cè)試用例文件(test*.py),并將查找到的測(cè)試用例組裝到測(cè)試 套件,因此可以直接通過(guò)run()方法執(zhí)行discover。用法如下:
discover=unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')unittest.skip():裝飾器,當(dāng)運(yùn)行用例時(shí),有些用例可能不想執(zhí)行等,可用裝飾器暫時(shí)屏蔽該條測(cè)試用例。一種常見(jiàn)的用法就是比如說(shuō)想調(diào)試某一個(gè)測(cè)試用例,想先屏蔽其他用例就可以用裝飾器屏蔽。
@unittest.skip(reason):?skip(reason)裝飾器:無(wú)條件跳過(guò)裝飾的測(cè)試,并說(shuō)明跳過(guò)測(cè)試的原因。
@unittest.skipIf(reason):?skipIf(condition,reason)裝飾器:條件為真時(shí),跳過(guò)裝飾的測(cè)試,并說(shuō)明跳過(guò)測(cè)試的原因。
@unittest.skipUnless(reason):?skipUnless(condition,reason)裝飾器:條件為假時(shí),跳過(guò)裝飾的測(cè)試,并說(shuō)明跳過(guò)測(cè)試的原因。
@unittest.expectedFailure(): expectedFailure()測(cè)試標(biāo)記為失敗。
2、TestCase類(lèi)的屬性如下:
['__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']
說(shuō)明:
setUp():setUp()方法用于測(cè)試用例執(zhí)行前的初始化工作。如測(cè)試用例中需要訪問(wèn)數(shù)據(jù)庫(kù),可以在setUp中建立數(shù)據(jù)庫(kù)連接并進(jìn)行初始化。如測(cè)試用例需要登錄web,可以先實(shí)例化瀏覽器。
tearDown():tearDown()方法用于測(cè)試用例執(zhí)行之后的善后工作。如關(guān)閉數(shù)據(jù)庫(kù)連接。關(guān)閉瀏覽器。
assert*():一些斷言方法:在執(zhí)行測(cè)試用例的過(guò)程中,最終用例是否執(zhí)行通過(guò),是通過(guò)判斷測(cè)試得到的實(shí)際結(jié)果和預(yù)期結(jié)果是否相等決定的。
assertEqual(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a和b是否相等,相等則測(cè)試用例通過(guò)。
assertNotEqual(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a和b是否相等,不相等則測(cè)試用例通過(guò)。
assertTrue(x,[msg='測(cè)試失敗時(shí)打印的信息']):斷言x是否True,是True則測(cè)試用例通過(guò)。
assertFalse(x,[msg='測(cè)試失敗時(shí)打印的信息']):斷言x是否False,是False則測(cè)試用例通過(guò)。
assertIs(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是否是b,是則測(cè)試用例通過(guò)。
assertNotIs(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是否是b,不是則測(cè)試用例通過(guò)。
assertIsNone(x,[msg='測(cè)試失敗時(shí)打印的信息']):斷言x是否None,是None則測(cè)試用例通過(guò)。
assertIsNotNone(x,[msg='測(cè)試失敗時(shí)打印的信息']):斷言x是否None,不是None則測(cè)試用例通過(guò)。
assertIn(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是否在b中,在b中則測(cè)試用例通過(guò)。
assertNotIn(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是否在b中,不在b中則測(cè)試用例通過(guò)。
assertIsInstance(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是是b的一個(gè)實(shí)例,是則測(cè)試用例通過(guò)。
assertNotIsInstance(a,b,[msg='測(cè)試失敗時(shí)打印的信息']):斷言a是是b的一個(gè)實(shí)例,不是則測(cè)試用例通過(guò)。
?
3、TestSuite類(lèi)的屬性如下:(組織用例時(shí)需要用到)
['__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']
說(shuō)明:
addTest():?addTest()方法是將測(cè)試用例添加到測(cè)試套件中,如下方,是將test_baidu模塊下的BaiduTest類(lèi)下的test_baidu測(cè)試用例添加到測(cè)試套件。
suite = unittest.TestSuite() suite.addTest(test_baidu.BaiduTest('test_baidu')) ?4、TextTextRunner的屬性如下:(組織用例時(shí)需要用到)
['__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']
說(shuō)明:
run():?run()方法是運(yùn)行測(cè)試套件的測(cè)試用例,入?yún)閟uite測(cè)試套件。
runner = unittest.TextTestRunner() runner.run(suite)四、使用unittest框架編寫(xiě)測(cè)試用例思路
設(shè)計(jì)基本思路如下,新建一個(gè)test.py文件(名稱(chēng)自定義),代碼如下
1 import unittest 2 3 4 class Test(unittest.TestCase): 5 # 定義測(cè)試類(lèi),父類(lèi)為unittest.TestCase 6 # 定義setUp()方法用于測(cè)試用例執(zhí)行前的初始化工作。 7 def setUp(self): 8 # 注意,所有類(lèi)中方法的入?yún)閟elf,定義方法的變量也要“self.變量” 9 self.number = input('Enter a number:') 10 self.number = int(self.number) # 注意,輸入的值為字符型的需要轉(zhuǎn)為int型 11 12 # 定義測(cè)試用例,以“test_”開(kāi)頭命名的方法,方法的入?yún)閟elf 13 # 可定義多個(gè)測(cè)試用例 14 # 最重要的就是該部分 15 def test_case1(self): 16 self.assertEqual(self.number, 10, msg = 'Your input is not 10') 17 18 def test_case2(self): 19 print(self.number) 20 self.assertEqual(self.number, 20, msg = 'Your input is not 20') 21 22 @unittest.skip('暫時(shí)跳過(guò)用例3的測(cè)試') 23 def test_case3(self): 24 print(self.number) 25 self.assertEqual(self.number, 30, msg = 'Your input is not 30') 26 27 # 定義tearDown()方法用于測(cè)試用例執(zhí)行之后的善后工作。 28 # 注意,方法的入?yún)閟elf 29 def tearDown(self): 30 print('Test over') 31 32 33 # 執(zhí)行測(cè)試用例方案一如下: 34 # unittest.main()方法會(huì)搜索該模塊下所有以test開(kāi)頭的測(cè)試用例方法,并自動(dòng)執(zhí)行它們。 35 # 執(zhí)行順序是命名順序:先執(zhí)行test_case1,再執(zhí)行test_case2 36 if __name__ == '__main__': 37 unittest.main() 38 39 # 執(zhí)行測(cè)試用例方案二如下(以下整段代碼塊替換方案一的代碼塊): 40 # 1.先構(gòu)造測(cè)試集2.實(shí)例化測(cè)試套件 41 if __name__ == '__main__': 42 suite = unittest.TestSuite() 43 # 將測(cè)試用例加載到測(cè)試套件中。 44 # 執(zhí)行順序是安裝加載順序:先執(zhí)行test_case2,再執(zhí)行test_case1 45 suite.addTest(Test('test_case2')) 46 suite.addTest(Test('test_case1')) 47 # 執(zhí)行測(cè)試用例,實(shí)例化TextTestRunner類(lèi),verbosity參數(shù)可以控制執(zhí)行結(jié)果的輸出,0 是簡(jiǎn)單報(bào)告、1 是一般報(bào)告、2 是詳細(xì)報(bào)告 48 runner = unittest.TextTestRunner(verbosity = 2) 49 # 使用run()方法運(yùn)行測(cè)試套件(即運(yùn)行測(cè)試套件中的所有用例) 50 runner.run(suite) 51 52 # 執(zhí)行測(cè)試用例方案三如下(以下整段代碼塊替換方案一的代碼塊): 53 # 構(gòu)造測(cè)試集(簡(jiǎn)化了方案二中先要?jiǎng)?chuàng)建測(cè)試套件然后再依次加載測(cè)試用例) 54 # 執(zhí)行順序同方案一:執(zhí)行順序是命名順序:先執(zhí)行test_case1,再執(zhí)行test_case2 55 if __name__ == '__main__': 56 test_dir = './' 57 # 通過(guò)該類(lèi)下面的discover()方法可自動(dòng)加載測(cè)試目錄test_dir匹配查找測(cè)試用例文件(test_*.py),并將查找到的測(cè)試用例組裝到測(cè)試套件discover中 58 discover = unittest.defaultTestLoader.discover(test_dir, pattern = 'test_*.py') 59 runner = unittest.TextTestRunner() 60 runner.run(discover)執(zhí)行py文件:python test.py
結(jié)果:
方案一:因?yàn)橄葓?zhí)行test_case1,再執(zhí)行test_case2,所以第一次輸入10時(shí),執(zhí)行通過(guò),返回.?第二次輸入10時(shí),執(zhí)行不通過(guò),返回F,最終一個(gè)用例通過(guò),一個(gè)用例失敗,還有一個(gè)用例是直接跳過(guò)的(裝飾器)
方案二:因?yàn)橄葓?zhí)行test_case2,再執(zhí)行test_case1,所以第一次輸入10時(shí),執(zhí)行不通過(guò),返回F?, 第二次輸入10時(shí),執(zhí)行通過(guò),返回.?,最終一個(gè)用例通過(guò),一個(gè)用例失敗。
方案三:因?yàn)橄葓?zhí)行test_case1,再執(zhí)行test_case2,所以第一次輸入10時(shí),執(zhí)行通過(guò),返回.?第二次輸入10時(shí),執(zhí)行不通過(guò),返回F,最終一個(gè)用例通過(guò),一個(gè)用例失敗,還有一個(gè)用例是直接跳過(guò)的(裝飾器)。
五、測(cè)試報(bào)告輸出:
1.?那如何生成一個(gè)測(cè)試報(bào)告呢,需要加入另外一個(gè)模塊了,HTMLTestRunner,這個(gè)模塊需要自己安裝,使用執(zhí)行測(cè)試用例就會(huì)生成一個(gè)html的測(cè)試報(bào)告
HTMLTestRunner地址:http://tungwaiyip.info/software/HTMLTestRunner.html
注意區(qū)分python的版本:2.x與3.x
下載完成后,放到C:\python35\Lib路徑里面,然后修改test.py文件
import HTMLTestRunner #導(dǎo)入報(bào)告模塊#在if __name__ == '__main__'代碼塊中,用下面代碼塊替換runner = unittest.TextTestRunner()with open('HTMLReport.html', 'w') as f:runner = HTMLTestRunner(stream=f,title='MathFunc Test Report',description='generated by HTMLTestRunner.',verbosity=2)runner.run(suite)
再執(zhí)行test.py文件后,后在test.py所在目錄中生成HTMLReport.html;這個(gè)路徑可以自行指定
六、測(cè)試日志輸出:
1. 可以在運(yùn)行的時(shí)候輸出日志文件,這個(gè)不是那么必要
修改test.py
1 import unittest 2 import time,os,sys,logging 3 4 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + r'\..') # 返回腳本的路徑 5 logging.basicConfig(level=logging.DEBUG, 6 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', 7 datefmt='%a, %d %b %Y %H:%M:%S', 8 filename='log_test.log', 9 filemode='w') 10 logger = logging.getLogger() 11 class Test(unittest.TestCase): 12 #代碼略執(zhí)行test.py文件后,后在test.py所在目錄中生產(chǎn)log_test.log
在上面代碼塊中的第5行:
INFO:處理請(qǐng)求或者狀態(tài)變化等日常事務(wù)(一般詳細(xì))DEBUG:調(diào)試過(guò)程中使用DEBUG等級(jí),如算法中每個(gè)循環(huán)的中間狀態(tài)(較詳細(xì))
七、總代碼示例:
1. Tset.py文件
# -*- coding:utf-8 -*-import unittest import time,os,sys,logging import HTMLTestRunnersys.path.append(os.path.dirname(os.path.abspath(__file__)) + r'\..') # 返回腳本的路徑 logging.basicConfig(level=logging.DEBUG,format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%a, %d %b %Y %H:%M:%S',filename='log_test.log',filemode='w') logger = logging.getLogger() class MyTest(unittest.TestCase): # 繼承unittest.TestCasedef setUp(self):# 每個(gè)測(cè)試用例執(zhí)行之前做操作print('執(zhí)行用例開(kāi)始')def tearDown(self):# 每個(gè)測(cè)試用例執(zhí)行之后做操作print('執(zhí)行用例結(jié)束')@classmethoddef tearDownClass(self):# 必須使用 @ classmethod裝飾器, 所有test運(yùn)行完后運(yùn)行一次print('--------測(cè)試執(zhí)行結(jié)束--------')@classmethoddef setUpClass(self):# 必須使用@classmethod 裝飾器,所有test運(yùn)行前運(yùn)行一次print('--------測(cè)試執(zhí)行開(kāi)始--------')def test_1_add(self):"""添加類(lèi)別信息"""logger.info("Now: %r", '執(zhí)行添加')self.assertEqual(1, 1)def test_2_que(self):"""查詢(xún)類(lèi)別信息"""logger.info("Now: %r", '執(zhí)行查詢(xún)')self.assertEqual(2, 2)if __name__ == '__main__':current_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))test_suite = unittest.TestSuite() # 創(chuàng)建一個(gè)測(cè)試集合test_suite.addTest(MyTest('test_1_add'))test_suite.addTest(MyTest('test_2_que'))# 測(cè)試套件中添加測(cè)試用例# test_suite.addTest(unittest.makeSuite(MyTest))#使用makeSuite方法添加所有的測(cè)試方法report_path = current_time + '.html' # 生成測(cè)試報(bào)告的路徑fp = open(report_path, "wb")runner = HTMLTestRunner.HTMLTestRunner(stream = fp,title = '自動(dòng)化測(cè)試報(bào)告',description = '用例執(zhí)行情況:',verbosity = 2)runner.run(test_suite)fp.close()?
轉(zhuǎn)載于:https://www.cnblogs.com/monogem/p/9755179.html
總結(jié)
以上是生活随笔為你收集整理的selenium自动化测试、Python单元测试unittest框架以及测试报告和日志输出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 1.Lambda表达式(新手写的!新手写
- 下一篇: Python 学习第三部分函数——第一章