《帶你裝B,帶你飛》pytest成魔之路4 - fixture 之大解剖

1. 簡介

fixture是pytest的一個閃光點,pytest要精通怎麼能不學習fixture呢?跟着我一起深入學習fixture吧。其實unittest和nose都支持fixture,但是pytest做得更炫。
fixture是pytest特有的功能,它用pytest.fixture標識,定義在函數前面。在你編寫測試函數的時候,你可以將此函數名稱做爲傳入參數,pytest將會以依賴注入方式,將該函數的返回值作爲測試函數的傳入參數。
fixture有明確的名字,在其他函數,模塊,類或整個工程調用它時會被激活。
fixture是基於模塊來執行的,每個fixture的名字就可以觸發一個fixture的函數,它自身也可以調用其他的fixture。
我們可以把fixture看做是資源,在你的測試用例執行之前需要去配置這些資源,執行完後需要去釋放資源。比如module類型的fixture,適合於那些許多測試用例都只需要執行一次的操作。
fixture還提供了參數化功能,根據配置和不同組件來選擇不同的參數。
fixture主要的目的是爲了提供一種可靠和可重複性的手段去運行那些最基本的測試內容。比如在測試網站的功能時,每個測試用例都要登錄和退出,利用fixture就可以只做一次,否則每個測試用例都要做這兩步也是冗餘。
pytest 提供的 fixture 實現 unittest 中 setup/teardown 功能,可以在每次執行case之前初始化數據。不同點是,fixture 可以只在執行某幾個特定 case 前運行,只需要在運行 case 前調用即可。比 setup/teardown 使用起來更靈活。上一篇講到用例加setup和teardown可以實現在測試用例之前或之後加入一些操作,但這種是整個腳本全局生效的,如果我想實現以下場景:用例1需要先登錄,用例2不需要登錄,用例3需要先登錄。很顯然這就無法用setup和teardown來實現了。這就是本篇學習的目的,自定義測試用例的預置條件

2. fixture scope 作用範圍

 先看下 fixture 函數的定義:

  使用裝飾器標記fixture的功能 可以使用此裝飾器(帶或不帶參數)來定義fixture功能。 fixture功能的名稱可以在以後使用 引用它會在運行測試之前調用它:test模塊或類可以使用pytest.mark.usefixtures(fixturename標記。 測試功能可以直接使用fixture名稱作爲輸入參數,在這種情況下,夾具實例從fixture返回功能將被注入。

def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
    """
    :arg scope:    可選四組參數:function(默認)、calss、module、package/session

    :arg params:   一個可選的參數列表,它將導致多個參數調用fixture函數和所有測試使用它。

    :arg autouse:  如果爲True,則fixture func將爲所有測試激活可以看到它。如果爲False(默認值),則需要顯式激活fixture。

    :arg ids:      每個參數對應的字符串id列表,因此它們是測試id的一部分。如果沒有提供id,它們將從參數中自動生成。

    :arg name:     fixture的名稱。 這默認爲裝飾函數的名稱。 如果fixture在定義它的同一模塊中使用,夾具的功能名稱將被請求夾具的功能arg遮蔽; 解決這個問題的一種方法是將裝飾函數命名 “fixture_ <fixturename>”然後使用”@ pytest.fixture(name ='<fixturename>')”。
  """

 重點說下 scope 四組參數的意義:

  • function:每個方法(函數)都會執行一次。

  • class:每個類都會執行一次。類中有多個方法調用,只在第一個方法調用時執行。

  • module:一個 .py 文件執行一次。一個.py 文件可能包含多個類和方法。

  • package/session:多個文件調用一次,可以跨 .py 文件。

 在所需要調用的函數前面加個裝飾器 @pytest.fixture()。舉一個簡單的例子:

3. fixture 優點

1.firture相對於setup和teardown來說應該有以下幾點優勢:

  • 命名方式靈活,不侷限於setup和teardown這幾個命名
  • conftest.py 配置裏可以實現數據共享,不需要import就能自動找到一些配置
  • scope=”module” 可以實現多個.py跨文件共享前置
  • scope=”session” 以實現多個.py跨文件使用一個session來完成多個用例

4. fixture參數傳入(scope=”function”)

例如這樣的測試場景:宏哥這裏就拿博客園舉個例子:

測試用例1:需要登錄博客園,發佈隨筆

測試用例2:不需要登錄博客園,瀏覽文章

測試用例3:需要登錄博客園,刪除隨筆

4.1 代碼實現:

把一個函數定義爲Fixture很簡單,只能在函數聲明之前加上“@pytest.fixture”。其他函數要來調用這個Fixture,只用把它當做一個輸入的參數即可。

4.2 參考代碼:

# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-09
@author: 北京-宏哥
Project:《帶你裝B,帶你飛》pytest修仙之路4 - fixture簡介 & yield
'''
# 3.導入模塊
#  content of  test_bjhg_class1.py

import pytest

# 不帶參數時默認scope="function"
@pytest.fixture(scope='function')
def login():
    print("登錄博客園")

def test_1(login):
    print('測試用例1,登錄博客園之後發佈隨筆111')

def test_2():
    print('測試用例2,不需要登錄博客園,瀏覽文章222')

def test_3(login):
    print('測試用例2,登錄博客園之後刪除隨筆333')
if __name__ == "__main__":
    pytest.main(["-s", "test_bjhg_class1.py"])

4.3 運行結果:

運行代碼後,控制檯打印如下圖的結果

下面是運行結果,test_1和test_3運行之前都調用了login,也就是login執行了兩次。默認情況下,fixture是每個測試用例如果調用了該fixture就會執行一次的。

2.如果@pytest.fixture()裏面沒有參數,那麼默認scope=”function”,也就是此時的級別的function,針對函數有效。

5. conftest.py配置

5.1 conftest.py配置需要注意以下點:

conftest.py配置腳本名稱是固定的,不能改名稱

conftest.py與運行的用例要在同一個pakage下,並且有init.py文件

不需要import導入 conftest.py,pytest用例會自動查找

1.上面一個測試場景是在同一個.py文件中,多個用例調用一個登陸功能,如果有多個.py的文件都需要調用這個登陸功能的話,那就不能把登陸寫到用例裏面去了。
此時應該要有一個配置文件,單獨管理一些預置的操作場景,pytest裏面默認讀取conftest.py裏面的配置

5.2 conftest.py

5.2.1 代碼實現:

5.2.2 參考代碼:
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-09
@author: 北京-宏哥
Project:《帶你裝B,帶你飛》pytest修仙之路4 - fixture簡介 & yield
'''
# 3.導入模塊
#  content of  conftest.py

import pytest

# 不帶參數時默認scope="function"
@pytest.fixture(scope='function')
def login():
    print("登錄博客園")

5.3 test_fix1.py

5.3.1 代碼實現:

5.3.2 參考代碼:
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-09
@author: 北京-宏哥
Project:《帶你裝B,帶你飛》pytest修仙之路4 - fixture簡介 & yield
'''
# 3.導入模塊
#  content of  test_fix1.py

import pytest

def test_1(login):
    print('測試用例1,登錄博客園之後發佈隨筆111')

def test_2():
    print('測試用例2,不需要登錄博客園,瀏覽文章222')

def test_3(login):
    print('測試用例2,登錄博客園之後刪除隨筆333')
if __name__ == "__main__":
    pytest.main(["-s", "test_fix1.py"])
5.3.3 運行結果:

運行代碼後,控制檯打印如下圖的結果

5.4 test_fix2.py

5.4.1 代碼實現:

5.4.2 參考代碼:
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-09
@author: 北京-宏哥
Project:《帶你裝B,帶你飛》pytest修仙之路4 - fixture簡介 & yield
'''
# 3.導入模塊
#  content of  test_fix2.py

import pytest


def test_4(login):
    print('測試用例4,登錄博客園之後修改頭像444')

def test_5():
    print('測試用例5,不需要登錄博客園,瀏覽首頁555')

def test_6(login):
    print('測試用例6,登錄博客園之後保存隨筆666')
if __name__ == "__main__":
    pytest.main(["-s", "test_fix2.py"])
5.4.3 運行結果:

運行代碼後,控制檯打印如下圖的結果

單獨運行test_fix1.py和test_fix2.py都能調用到login()方法,這樣就能實現一些公共的操作可以單獨拿出來了

6. 小結

  如果你的程序出現了下面的錯誤,就是開始忘記添加‘import pytest',所以不要忘記哦。

=================================== ERRORS ====================================
_________________ ERROR collecting test_fixture_decorator.py __________________
test_fixture_decorator.py:2: in <module>
    @pytest.fixture()
E   NameError: name 'pytest' is not defined
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.36 seconds ===========================

  好了,今天的分享就到這裏吧!!!謝謝各位的耐心閱讀。有問題加羣交流討論!!!

 

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

別忘了點 推薦 留下您來過的痕跡

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章