測試框架實踐--TestFixture

iTesting2019-04-14 03:25:39

iTesting,愛測試,愛分享


前面幾期分享我實現了一個可以併發運行的”框架“, 其實只能叫半成品, 但好歹可以併發運行, 測試用例動態挑選了。那麼還少了什麼呢?

一個測試類,通常有多個測試方法,有時候一個或多個測試方法都需要某些共用的”數據“, 比如説都要訪問某個數據庫的某張表,比如説都需要起瀏覽器,都需要調用post方法等。 這個時候每個測試用例單獨寫就顯得很多餘,TestFixture就應運而生。

我們先來看下Test Fixture的定義:

A test fixture represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process.

由此可見,Test Fixture用在測試方法前,或者測試方法後,主要功能是提供一些測試需要用的裝置,這些裝置可以是數據,可以是環境配置也可以是一個運行前狀態。

Fixture有下面兩種:
1.setup(), teardown()的方式,分別在每個測試方法執行前後執行。
2.setUpClass(), tearDownClass()的方式,分別在每個測試類執行前後執行, setUpClass()和tearDownClass()只會執行一次,即使這個測試類有多個測試函數。

我們在實現這個之前,先看下上次我們實現併發時,真正執行一個測試函數的代碼塊, 它的代碼:

 1def f(case):
2    name, func, value 
case
3    try:
4        if value:
5            func.__call__(name, *value)
6        else:
7            func.__call__(name)
8    except:
9        # traceback.print_exc()
10        cases_run_fail.append(name)
11    else:
12        cases_run_success.append(name)
13    return cases_run_fail, cases_run_success

這個代碼塊是針對每一個測試函數的,那麼我們多線程運行時,每個測試函數都會執行這段代碼,這樣就好辦了,直接把setup和teardown加進來就能實現每個測試函數都執行setup和teardown方法了。

 1def f(case):
2    cls, name, func, value 
case
3    try:
4        # Run setUP method for each test method.
5        #看這裏
6        getattr(cls, "setUp").__call__(cls) 
7
8        if value:
9            func.__call__(name, *value)
10        else:
11            func.__call__(name)
12
13        #看這裏    
14        # Run tearDown method for each test method.
15        getattr(cls, "tearDown").__call__(cls)
16    except:
17        # traceback.print_exc()
18        cases_run_fail.append(name)
19    else:
20        cases_run_success.append(name)
21    return cases_run_fail, cases_run_success

可以看到這個函數傳入的參數也改變了,case的值里加入了cls這個類。要達成這個效果, 相當於如何根據測試方法找到所屬的測試類,利用inspect模塊很簡單的就拿到了,當然你也可以用__name__拿到函數名。

setup和teardown這兩個方法每個測試用例都會執行,看到這裏想明白了嗎?這就意味着如果你在這裏做初始化瀏覽器的操作,那麼這個框架就可以做UI自動化,如果你做的是HTTP的get或者post的操作,那麼這個框架也就是API接口框架。

setup和teardown實現了。我們再來看下setUpClass, tearDownClass的實現。那麼我是怎麼共享線程間的變量呢?

常規情況下,我們可以用數據持久化的方式實現,具體來説,就是每個測試函數執行時候先去找一個文件,這個文件在就不再執行setUpClass,當然你得做好線程安全。

大家還記得我的多線程是怎麼實現的呢?

1#多線程部分代碼
2with ThreadPool(number_of_threads) as p:
3    p.map(f, cases_to_run)
4p.close()
5p.join()

對於每一個線程,都去調用f函數,這個函數就是上方實現了每個函數setup和teardown的f。

現在好了,我們用map可以不關心這些,直接嵌套實現:

1.我把所有的用例重新組織,形成一種特殊的數據結構,具體來説,就是每個測試類和屬於這個測試類的所有函數, 以如下方式組織(cls, [cls.method1, cls.method2]),最終所有的測試類,在放到一個列表對象裏。
2.這樣第一層次的併發,是基於測試類的,然後針對每一個測試類,我再進行併發。

具體代碼如下:

 1#部分代碼
2def run(case):
3    #cls是測試類
4    cls = case[0]
5    #func_pack是測試函數及所有參數
6    func_pack = case[1
7
8    #實現setUPClass
9    #到這一層只是類併發,真正的測試函數還沒有併發。
10    #setUPClass應該做到測試類有設置就執行,否則就不執行。
11    handle_before_class_fixture(cls)
12
13    p = ThreadPool(number_of_threads)
14    #真正的測試函數併發
15    #這個func_run就相當於我們之前的f。
16    p.map(func_run, func_pack)
17    p.close()
18    p.join()
19
20    #實現tearDownClass
21    handle__after_class_fixture(cls)

為了驗證正確性,我把我們的測試類和方法改進如下:

 1@TestClass()
2class TestSumData:
3    @BeforeClass()
4    def before_class(cls):
5        print("haha")
6        cls.status = 1
7    def setUp(self):
8        print("Now starting")
9    @data_provider([(123), (459)])
10    @Test()
11    def test_sum_data(self, x, y, z):
12        print('Case {0} are running with data {1} -- thread id {2}'.format(
13            self, (x, y, z), current_process()))
14        print(self.status)
15        assert SumData().sum_data(x, y) == z
16    @Test(enabled=True)
17    def test_sum_data2(self):
18        print('Case {0} are running with data {1} -- thread id {2}'.format(
19            self, None , current_process()))
20        print(self.status)
21        assert SumData().sum_data(45) == 7
22    def tearDown(self):
23        print("Now finished")
24    @AfterClass()
25    def after_class(cls):
26        cls.status =0

跑一下看看:

可以看到,cls.status這個共享數據,成功被測試函數接收到了。 setUpClass(我這裏對應before_class)也成功運行, 且只運行了一次。

毫無破綻:)

到此為止,我已經實現了一個測試框架的絕大數功能,下一步,就要改造我的log還有接收用户參數了,敬請期待。

歷史文章:
Python數據驅動實踐(一)–ddt實現數據驅動
Python數據驅動實踐(二)–教你用Python實現數據驅動
Python數據驅動實踐(三)–動態添加測試用例
Python測試框架實現(四)–動態挑選測試用例
Python測試框架實現(五)–多線程


 -  End  -

作者:

Kevin Cai, 江湖人稱蔡老師。

兩性情感專家,非著名測試開發。

技術路線的堅定支持者,始終相信Nobody can be somebody。      

                     

· 猜你喜歡的文章 ·

功能測試進階系列直播説明

你真的能看懂測試報告嗎?

如何模擬不同網絡環境及設置丟包率?



https://hk.wxwenku.com/d/200085805