Python3的unittest用例按编写顺序执行
unittest是Python標(biāo)準(zhǔn)庫自帶的單元測試框架,是Python版本的JUnit,關(guān)于unittest框架的使用,官方文檔非常詳細(xì),網(wǎng)上也有不少好的教程,這里就不多說了。
本文主要分享在使用unittest的過程中,做的一些擴(kuò)展嘗試。先上一個(gè)例子。
import unittestclass TestLegion(unittest.TestCase): def test_create_legion(self): """創(chuàng)建軍團(tuán) :return: """ def test_bless(self): """ 公會祈福 :return: """ def test_receive_bless_box(self): """ 領(lǐng)取祈福寶箱 :return: """ def test_quit_legion(self): """退出軍團(tuán) :return: """這是一個(gè)標(biāo)準(zhǔn)的使用unittest進(jìn)行測試的例子,寫完后心里美滋滋,嗯,就按照這個(gè)順序測就可以了。結(jié)果一運(yùn)行。
什么鬼。執(zhí)行的順序亂了。第一個(gè)執(zhí)行的測試用例并不是創(chuàng)建軍團(tuán),而是公會祈福,此時(shí)玩家還沒創(chuàng)建軍團(tuán),進(jìn)行公會祈福的話會直接報(bào)錯,導(dǎo)致用例失敗。
到這里有些同學(xué)會想說,為什么要讓測試用例之間有所依賴呢?
的確,如果完全沒依賴,測試用例的執(zhí)行順序是不需要關(guān)注的。但是這樣對于用例的設(shè)計(jì)和實(shí)現(xiàn),要求就高了許多。而對游戲來說,一個(gè)系統(tǒng)內(nèi)的操作,是有很大的關(guān)聯(lián)性的。以軍團(tuán)為例,軍團(tuán)內(nèi)的每個(gè)操作都有一個(gè)前提,你需要加入一個(gè)軍團(tuán)。所以要實(shí)現(xiàn)用例之間的完全解耦,需要每個(gè)用例開始之前,檢測玩家的軍團(tuán)狀態(tài)。
如果可以控制測試用例的執(zhí)行順序,按照功能玩法流程一遍走下來,節(jié)省的代碼量是非常可觀的,閱讀測試用例也會清晰許多。
如何控制unittest用例執(zhí)行的順序呢?
我們先看看,unittest是怎么樣對用例進(jìn)行排序的。在loader.py的loadTestsFromTestCase方法里邊,調(diào)用了getTestCaseNames方法來獲取測試用例的名稱
def getTestCaseNames(self, testCaseClass): """Return a sorted sequence of method names found within testCaseClass """ def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix): return attrname.startswith(prefix) and \ callable(getattr(testCaseClass, attrname)) testFnNames = list(filter(isTestMethod, dir(testCaseClass))) if self.sortTestMethodsUsing: testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing)) return testFnNames可以看到,getTestCaseNames方法對測試用例的名稱進(jìn)行了排序
testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))看看排序方法
def three_way_cmp(x, y): """Return -1 if x < y, 0 if x == y and 1 if x > y""" return (x > y) - (x < y)根據(jù)排序規(guī)則,unittest執(zhí)行測試用例,默認(rèn)是根據(jù)ASCII碼的順序加載測試用例,數(shù)字與字母的順序?yàn)?#xff1a;0-9,A-Z,a-z。
做個(gè)實(shí)驗(yàn):
import functoolscase_names = ["test_buy_goods", "test_Battle", "test_apply", "test_1_apply"] def three_way_cmp(x, y): """Return -1 if x < y, 0 if x == y and 1 if x > y""" return (x > y) - (x < y) case_names.sort(key=functools.cmp_to_key(three_way_cmp)) print(case_names) output:['test_1_apply', 'test_Battle', 'test_apply', 'test_buy_goods']基于unittest的機(jī)制,如何控制用例執(zhí)行順序呢?查了一些網(wǎng)上的資料,主要介紹了兩種方式:
方式1,通過TestSuite類的addTest方法,按順序加載測試用例:
suite = unittest.TestSuite() suite.addTest(TestLegion("test_create_legion")) suite.addTest(TestLegion("test_bless")) suite.addTest(TestLegion("test_receive_bless_box")) suite.addTest(TestLegion("test_quit_legion")) unittest.TextTestRunner(verbosity=3).run(suite) ?方式2,通過修改函數(shù)名的方式:
class TestLegion(unittest.TestCase): def test_1_create_legion(self): """創(chuàng)建軍團(tuán) :return: """ def test_2_bless(self): """ 公會祈福 :return: """ def test_3_receive_bless_box(self): """ 領(lǐng)取祈福寶箱 :return: """ def test_4_quit_legion(self): """退出軍團(tuán) :return: """ ?看起來都能滿足需求,但是都不夠好用,繁瑣,代碼不好維護(hù)。
那就造個(gè)輪子吧
于是開始了utx這個(gè)小項(xiàng)目,那么如何在不改動代碼的情況下,讓測試用例按照編寫的順序依次執(zhí)行呢?
方案就是,在測試類初始化的時(shí)候,將測試方法按照編寫的順序,自動依次重命名為“test_1_create_legion”,“test_2_bless”,“test_3_receive_bless_box”等等,從而實(shí)現(xiàn)控制測試用例的執(zhí)行。
這就需要控制類的創(chuàng)建行為,Python提供了一個(gè)非常強(qiáng)力的工具:元類,在元類的__new__方法中,我們可以獲取類的全部成員函數(shù),另外基于Python3.6的字典底層重構(gòu)后,字典是有序的了,默認(rèn)順序和添加的順序一致。所以我們拿到的測試用例,就和編寫的順序一致了。
??
接下來,就是按照順序,依次改名了,定義一個(gè)全局的total_case_num變量,每次進(jìn)行改名的時(shí)候,total_case_num遞增+1,作為用例的id,加入到用例的名字當(dāng)中。
接下來是定義自己的TestCase類,繼承unittest.TestCase,使用上邊定義的元類
class _TestCase(unittest.TestCase, metaclass=Meta): def shortDescription(self): """覆蓋父類的方法,獲取函數(shù)的注釋 :return: """ doc = self._testMethodDoc doc = doc and doc.split()[0].strip() or None return doc最后一步,對unittest打一個(gè)猴子補(bǔ)丁,將unittest.TestCase替換為自定義的_TestCase
unittest.TestCase = _TestCase看下運(yùn)行效果,代碼和本文開始的例子一樣,只是多了一句utx庫的導(dǎo)入。
import unittest from utx import *class TestLegion(unittest.TestCase): def test_create_legion(self): """創(chuàng)建軍團(tuán) :return: """ def test_bless(self): """ 公會祈福 :return: """ def test_receive_bless_box(self): """ 領(lǐng)取祈福寶箱 :return: """ def test_quit_legion(self): """退出軍團(tuán) :return: """運(yùn)行效果:
?執(zhí)行順序就和我們的預(yù)期一致了~
基于這一套,開始加上其他的一些擴(kuò)展功能,比如
- 用例自定義標(biāo)簽,可以運(yùn)行指定標(biāo)簽的測試用例
- 數(shù)據(jù)驅(qū)動
- 檢測測試用例是否編寫了說明描述
- 執(zhí)行測試用例的時(shí)候,顯示執(zhí)行進(jìn)度
- setting類提供多個(gè)設(shè)置選項(xiàng)進(jìn)行配置
- 集成 ztest 和 BSTestRunner 生成測試報(bào)告,感謝兩位作者的測試報(bào)告模版
utx庫核心源碼不到200行,就不做過多講解了,直接去Github看吧
作者:煎煎煎餅 鏈接:https://www.jianshu.com/p/d65f97723af7 來源:簡書 簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。轉(zhuǎn)載于:https://www.cnblogs.com/songzhenhua/p/9690198.html
總結(jié)
以上是生活随笔為你收集整理的Python3的unittest用例按编写顺序执行的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 监控:系统构架重要的一环
- 下一篇: Python学习笔录