python中pytest的用法,pytest中fixture用法


pytest模塊的使用

pytest是第三方測試框架,是基於unittest的擴展框架,比unittest更簡潔,更高效。安裝pytest模塊使用pip install pytest即可。安裝好之后,到cmd中輸入pytest --version檢查是否安裝成功。

pytest運行方法

想要用pytest運行,首先要import pytest

比如創建一個demo.py文件,內容為:

import pytest         # 導入包

def test_sucess():   # 定義第一個測試用例,assert 0表示斷言失敗

    print("test sucess")

    assert 0

def test_fail():  # 定義第二個測試用例,沒有assert,默認成功

    print("test fail")

if __name__ == "__main__":

#定義一個列表,列表內容為測試文件名,也可以為元組,表示需要運行的文件為demo.py

test_list = ['demo.py']    

# 用pytest模塊的main()方法,參數為上面定義好的列表或者元組。

pytest.main(test_list)      

# pytest.main(['-s','demo.py'])  # 也可以這樣寫,這樣寫和上面那樣寫會運行結果會有所不同,可以自己試試看。

 

運行出來的結果是:

demo.py F.

F:表示測試失敗

.表示測試成功

也可以在cmd下面運行,先進入需要測試的文件夾路徑下,輸入命令:

pytest -s demo.py

這里要提醒一點,pytest必須遵循以下規則:

1、測試文件名必須以“test_”開頭或者以”_test”結尾

2、測試方法必須以“test_開頭

3、測試類命名以Test開頭

那么執行“pytest -s demo.py”這句話的時候,python就會自動去尋找test_開頭或者結尾的函數來執行。

 

setup()方法teardown()方法

setup()方法和teardown()方法是兩個特殊方法,setup()是在每一個編寫的測試用例執行前必然會執行的方法,teardown()方法是在每個測試用例執行后執行的方法。

比如上面的兩個測試用例,修改為:

class TestLogin:

    def setup(self):

        print("setup")

    def teardown(self):

        print("teardown")

 

    def test_sucess(self):

        print("test sucess")

    def test_fail(self):

        print("test fail")

上面這段代碼,定義了一個測試類,有兩個測試腳本test_sucesstest_fail,並且還增加了 setup()teardown()方法,此時去終端執行pytest -s demo.py,運行結果為:

setup       # setup方法打印出來的

test sucess     #第一個測試用例打印出來的

.teardown       #前面的點表示測試用例test_sucess執行成功,teardownteardown

方法打印出來的

setup      # 執行第二個測試用例的時候,會再次執行setup方法和teardown方法

test fail

.teardown

setup_class()teardown_class()方法

如果理解了上面講解的setupteardown方法,那么setup_classteardown_class方法也容易理解,這兩個方法是針對測試類的,是每一個類執行前后必須執行的方法。

class TestLogin:

    def setup_class(self):

        print("setup_class")

    def teardown_class(self):

        print("teardown_class")

 

    def setup(self):

        print("setup")

    def teardown(self):

        print("teardown")

 

    def test_sucess(self):

        print("test sucess")

    def test_fail(self):

        print("test fail")

運行結果為:

setup_class   # 類運行時先運行的方法

setup

test sucess

.teardown

setup

test fail

.teardown

teardown_class  # 類中的方法都運行結束后,再運行的方法

一般情況下,比如setup_class()方法內,可以放連接手機的代碼,對應的teardown_class方法就可以寫退出driver的代碼。也可以寫的setup里面,根據自己的需求來定,寫到setup中可能會更保險,寫case的時候也會更簡單,只是效率低了些。

pytest配置文件

pytest運行時可以有一個配置文件,名字為pytest.ini,並且這個配置文件名字必須這么寫,一般放到測試項目目錄中。

 

pytest.ini的內容為:

[pytest]

# 參數

addopts = -s

# 搜索哪個文件夾

testpaths = ./scripts

# 搜索的文件名以test_開頭

python_files = test_*.py

# 搜索的類名以Test開頭

python_classes = Test*

# 搜索的方法名以test_開頭

python_functions = test_*

 

有了這個配置文件,就等同於告訴了python要運行哪個文件夾下的哪些文件。到cmd中運行的時候,進入到你自己的項目路徑,不需要寫參數,直接輸入pytest就可以,就會按照你的配置文件來運行該運行的用例。

pytest常用插件

生成測試報告

pytest-html

安裝方式: pip install pytest-html

安裝成功后,運行的方式,有兩種,第一種:

在終端從pytest改成pytest --html=report/report.html

意思是創建一個文件夾為report,在report文件夾中創建一個html文件,該文件里面存的就是運行的測試用例結果

如圖:

 

 

 

第二種運行方式:

修改pytest.ini配置文件:

addopts = -s --html=report/report.html

然后在終端依舊輸入pytest運行,出來的結果是一樣的,推薦第二種方式,更簡單。

addopts還有很多其他參數,后面還會有講到一些

 

__pycache__錯誤如果是報這個錯誤,可能是在拷貝東西的時候出錯誤的,一般把文件夾中的__pycache__這個文件夾刪掉就好,這個是一個自動生成的編譯文件,在編譯的時候發現已經有這個文件了,就會報錯。

pytest控制函數執行順序

安裝插件:pip install pytest-ordering

這個插件的意義在於,如果你不想讓你的case按照從上往下的順序執行,就可以通過這個控件來控制一下。

具體用法如下:

import pytest       # 注意導包

class TestLogin:


    @pytest.mark.run(order=0)

    def test_sucess(self):     # 定義的第一個case,上面有一個裝飾器

        print("test sucess1")
 

    @pytest.mark.run(order=2)

    def test_sucess2(self):

        print("test sucess2")

 
        def test_fail(self):

        print("test fail3")
 

    @pytest.mark.run(order=1)

    def test_sucess4(self):

        print("test sucess4")

 

這里的裝飾器:@pytest.mark.run(order=n)

用法是固定的用法,就是上面這樣寫,唯一變化的是order的值nn可以為負數、正數、0,包括小數,執行順序和order的值有關系,可以自己試驗一下,order的值為0的時候是首先執行的用例,其次執行正數(由小到大的順序),再其次執行沒有此裝飾器做標記的,最后執行order為負數的(由小到大的順序)。

數字如果寫的一樣的話,就按照從上到下的順序了。

失敗重試

首先也安裝插件:pip install pytest-rerunfailures

用法一:命令行輸入命令:pytest --reruns n

n是重試的次數

比如:pytest --reruns 2,即失敗后再重試兩次,總共執行三次。

用法二,修改配置文件:

addopts = -s --html=report/report.html --reruns 3

修改完配置文件,直接命令行輸入pytest運行即可。

失敗重試的含義:assert 0或者assert False,都是return False的意思,失敗重試的意思就是遇到return False,就重跑這個case,直到跑完n次或者直到這個case返回了Ture就停止嘗試。

pytest高級用法

跳過測試函數

有些測試用例本在某種條件下不希望執行測試,那么就可以使用跳過測試函數:

skipifconditionreason=None

condition:跳過的條件,條件返回True的時候就跳過這個case。參數必須寫

reason:跳過的原因,內容可以不寫,參數必須有。

使用方法:

@pytest.mark.skipif(condition,reason=None)

 

比如:

@pytest.mark.skipif(True, reason='')

    def test_sucess4(self):

        print("test sucess4")

 

這樣執行的結果就會跳過這個case,控制台輸出的是一個ss表示跳過用例沒有執行。

結果:

scripts\test_login.py

test sucess2.

test sucess1.

s

test sucess4.

標記為預期失敗的函數

標記為預期失敗的意思就是這個case失敗了才是對的,我們期望它是失敗的。

xfailconditionreason=None

condition:這里如果返回True,意思就是希望是預期失敗的,False表示預期成功。

reason:原因,內容可以不寫,參數必須有。

使用方法:

@pytest.mark.xfail(condition,reason=None)

 

示例代碼:

class TestLogin:

 

    # 預期失敗,結果成功

    @pytest.mark.xfail(True, reason='')   # 這里返回True表示預期失敗

    def test_sucess1(self):

        print("test sucess1")

        assert True     # 結果返回True  這應該是一個失敗的用例

 

    # 預期失敗,結果失敗

    @pytest.mark.xfail(True, reason='')

    def test_sucess2(self):

        print("test sucess2")

        assert False

 

    # 預期成功,結果失敗

    @pytest.mark.xfail(False, reason='')  # False表示預期成果

    def test_fail(self):

        print('test fail')

        assert False

 

    # 預期成功,結果成功

    @pytest.mark.xfail(False, reason='')

    def test_sucess4(self):

        print("test sucess4")

        assert True

 

上面有四個用例,這四個用例,預期失敗結果失敗,或者預期成功結果成功,就算用例成功。其他都失敗,運行出來的結果為:

XPassed  XFailed  Failed  Passed

上面的用例,從運行結果的顏色上面來看,紅色表示失敗,綠色成功,橙色就是預期失敗結果失敗的。預期和結果是不一樣的,就會失敗。

另外可以看到結果的前面有的有一個XX表示這是一個預期失敗的用例。

 

函數數據參數化

這點非常重要。

方便測試函數獲取測試數據,

方法:parametrizeargnamesargvaluesindirect=Flaseids=Nonescope=None

常用參數:

argnames:參數名

argvalues:參數值,類型必須為list,參數只有一個時,格式為[value1,value2,value3...];參數有多個時,格式為[(參數1值,參數2...),(參數1值,參數2...)...]

使用方法:

@pytest.mark.parametrize(argnames,argvalues)

例如,一個參數的時候:

@pytest.mark.parametrize('search_values', ['wlan', '藍牙', '1'])

多個參數的時候:

@pytest.mark.parametrize(('username','password'),[(‘zhangsan’,’mima1’),(‘lisi’,‘mima2’)])    # 注意這里第一個參數是元組

 

參數有幾個,測試用例就運行幾次。

下面這個代碼示例,是進入到設置頁面,點擊放大鏡,依次搜索’wlan’,’藍牙’,’1’

import time

import pytest

from appium import webdriver

 

class TestSearch:

    # 連接手機,進入到設置頁面

    def setup(self):

        server = r'http://localhost:4723/wd/hub'  # Appium Server, 端口默認為4723

        desired_capabilities = {

            'platformName': 'Android',

            'deviceName': '127.0.0.1:62001',  # 需替換成你的deviceName

            'platformVersion': '5.1.1',

            'appPackage': 'com.android.settings',

            'appActivity': '.Settings'

        }

        self.driver = webdriver.Remote(server, desired_capabilities)


# 參數化方法,注意參數名和下面用到的參數名一致

    @pytest.mark.parametrize('search_values', ['wlan', '藍牙', '1'])

    def test_search(self, search_values):   # 參數名,和上面的參數化方法中的一致

        # 點擊放大鏡按鈕

        self.driver.find_element_by_id('com.android.settings:id/search').click()

        time.sleep(2)

        # 輸入搜索內容

        self.driver.find_element_by_id('android:id/search_src_text').

        send_keys(search_values)

        print('search %s success' % search_values)

        assert 1

 

還有多個參數的情況,比如說測試登錄,需要輸入用戶名和密碼兩個參數,下面的代碼示例是在通訊錄添加姓名和電話號碼:

 

  # 連接手機,進入到通訊錄頁面

    def setup(self):

        server = r'http://localhost:4723/wd/hub'  # Appium Server, 端口默認為4723

        desired_capabilities = {

            'platformName': 'Android',

            'deviceName': '127.0.0.1:62001',  # 需替換成你的deviceName

            'platformVersion': '5.1.1',

            'appPackage': 'com.android.contacts',

            'appActivity': '.activities.PeopleActivity'

        }

        self.driver = webdriver.Remote(server, desired_capabilities)

    @pytest.mark.parametrize(('username', 'tel'), [('zhangsan', '123'), ('lisi', '456')])

    def test_add_tel(self, username, tel):

        # 進入手機通訊錄頁面,點擊新增聯系人

        self.driver.find_element_by_id('com.android.contacts:id/floating_action_button').click()

        time.sleep(2)

        # 我的手機添加通訊錄的時候會有一個彈框提示,所以有下面這句

        self.driver.find_element_by_xpath("//*[contains(@text, '本地')]").click()

        time.sleep(2)

        # 輸入姓名

        self.driver.find_element_by_xpath("//*[contains(@text, '姓名')]").send_keys(username)

        # 輸入電話

        self.driver.find_element_by_xpath("//*[contains(@text, '電話')]").send_keys(tel)

        # 點擊返回

        self.driver.keyevent(4)

 

pytest-fixture用法詳解

fixture簡介

fixture的目的是提供一個固定基線,在該基線上測試可以可靠地和重復地執行。fixture提供了區別於傳統單元測試(setup/teardown)有顯著改進:

  • 有獨立的命名,並通過聲明它們從測試函數、模塊、類或整個項目中的使用來激活。
  • 按模塊化的方式實現,每個fixture都可以互相調用。
  • fixture的范圍從簡單的單元擴展到復雜的功能測試,允許根據配置和組件選項對fixture和測試用例進行參數化,或者跨函數function、類class、模塊module或整個測試會話sessio范圍。

fixture用途

1.做測試前后的初始化設置,如測試數據准備,鏈接數據庫,打開瀏覽器等這些操作都可以使用fixture來實現

2.測試用例的前置條件可以使用fixture實現

3.支持經典的xunit fixture ,像unittest使用的setupteardown

4.fixture可以實現unittest不能實現的功能,比如unittest中的測試用例和測試用例之間是無法傳遞參數和數據的,但是fixture卻可以解決這個問題

fixture()方法

定義一個fixture,和定義普通函數差不多,只是多了一個裝飾器@pytest.fixture(),並且fixture命名不要以test_開頭,盡量和用例區別開。fixture是可以有返回值的,如果沒return,默認返回None。用例調用fixture的返回值,直接就是把fixture的函數名稱當成變量名稱

fixturescope='function'params=Noneautouse=Falseids=Nonename=None):

scope:有四個級別參數"function"(默認),"class""module""session"

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

autouse:如果True,則為所有測試用例不需要傳參也會調用這個fixture。如果為False則需要顯示的調用fixture

ids:每個字符串id的列表,每個字符串對應於params這樣他們就是測試ID的一部分。如果沒有提供ID它們將從params自動生成

namefixture的名稱。這默認為裝飾函數的名稱。如果fixture在定義它的統一模塊中使用,夾具的功能名稱將被請求夾具的功能arg遮蔽,解決這個問題的一種方法時將裝飾函數命令"fixture_<fixturename>"然后使用"@pytest.fixturename='<fixturename>'"

 

scope為默認值function

它的作用范圍是每個測試用例來之前運行一次,銷毀代碼在測試用例之后運行。

示例:

import pytest


@pytest.fixture()

def test1():

    a = 'su'

    print('\ntest1方法傳出a')

    return a
 

@pytest.fixture(scope='function')

def test2():

    b = ''

    print('\ntest2方法傳出b')

    return b

 

class TestFixture:


    def test_3(self, test1):

        name = 'su'

        print('找到name')

        assert test1 == name


    def test_4(self, test2):

        sex = ''

        print('找到sex')

        assert test2 == sex

 

執行的結果為:

test1方法傳出a

找到name

.

test2方法傳出b

找到sex

.
scope="class" fixture為class級別的時候,如果一個class里面有多個用例,都調用了此fixture,那么此fixture只在此class里所有用例開始前執行一次。 示例: 
@pytest.fixture(scope='class')

def test1():

    b = ''

    print('傳出了%s, 且只在class里所有用例開始前執行一次!!!' % b)

    return b
 

class TestCase:

    def test_3(self, test1):

        name = 'su'

        print('找到name')

        assert test1 == name


    def test_4(self, test1):

        sex = ''

        print('找到sex')

        assert test1 == sex


執行結果為:

傳出了男, 且只在class里所有用例開始前執行一次!!!

找到name

F

找到sex

 

scope="module"

在當前.py腳本里面所有用例開始前只執行一次

@pytest.fixture(scope='module')

scope="session"

fixturesession級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例的時候,如果多個用例只需調用一次fixture,那就可以設置為scope="session",並且寫到conftest.py文件里。

conftest.py文件名稱是固定的,pytest會自動識別該文件。放到項目的根目錄下就可以全局調用了,如果放到某個package下,那就在該package內有效。

fixture自動使用autouse=True

當用例很多的時候,每次都傳fixture這個參數,會很麻煩。fixture里面有個參數autouse,默認是False沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了

autouse設置為True,自動調用fixture功能

 

@pytest.fixture(scope='module', autouse=True)

def test1():

print('\n開始執行module')


@pytest.fixture(scope='class', autouse=True)

def test2():

print('\n開始執行class')


@pytest.fixture(scope='function', autouse=True)

def test3():

print('\n開始執行function')


# 上面定義fixture的時候設置了autouse=True,那么使用的時候就不需要傳參了,該調用的時候自動會調用

def test_a():

    print('---用例a執行---')

def test_d():

    print('---用例d執行---')

 

class TestCase:

    def test_b(self):

        print('---用例b執行---')

    def test_c(self):

        print('---用例c執行---')

 

fixture嵌套使用

這里還有需要注意的是,fixture可以嵌套使用,如果你想在一個fixture執行前先執行另一個fixture

order = []

@pytest.fixture

def f1(f3):                       # 這里f3這個fixture傳到f1這個fixture中

    order.append("f1")


@pytest.fixture()

def f3():

    order.append("f3")
 

def test_1(f1):   # 參數為f1,調用f1的時候會先執行f1的參數f3

    print(order)

    assert order == ["f3", "f1"]
 

執行結果:

['f3', 'f1']

 

fixture參數化

需要使用params參數進行參數化,然后通過request.param取出

例如,如果只有一個參數

@pytest.fixture(params=['', ''])    #將一個list給params參數

def fix(request):      # request參數必須這樣寫

    return request.param    # reques.param會依次將params里面的值返回去

 

def test_9(fix):

    print(fix)

 

運行結果:

男

.

女

.

 

 

 

如果有多個參數

li = [{'name':'zhangsan', 'pwd':'mima1'},{'name':'lisi', 'pwd':'mima2'}
 

@pytest.fixture(params=li)

def fix(request):

    return request.param

def test_9(fix):

    print(fix['name'])

    print(fix['pwd'])

 

fixture使用方式

第一種方式:

就是像上面用到的那樣,當成參數傳入到測試用例方法中。或者結合autouse=True的方式

第二種方式:

使用裝飾器@pytest.mark.usefixtures()修飾需要運行的用例

示例:

@pytest.fixture()

def test1():

    print('\n開始執行function')


@pytest.mark.usefixtures('test1')

def test_a():

    print('---用例a執行---')


@pytest.mark.usefixtures('test1')

class TestCase:

    def test_b(self):

        print('---用例b執行---')

    def test_c(self):

        print('---用例c執行---')

 

執行結果為:

開始執行function

---用例a執行---

.

開始執行function

---用例b執行---

.

開始執行function

---用例c執行---

.

 

也可以疊加使用,在上面那段代碼基礎上加入tese2,然后使用的時候寫兩個裝飾器:

@pytest.fixture()

def test2():

    print('\n開始執行function2')
 

@pytest.mark.usefixtures('test1')

@pytest.mark.usefixtures('test2')

def test_a():

print('---用例a執行---')

 

usefixtures與傳fixture區別

如果fixture有返回值,那么usefixture就無法獲取到返回值,這個是裝飾器usefixture與用例直接傳fixture參數的區別。

fixture需要用到return出來的參數時,只能使用參數名稱直接當參數傳入的方式,不需要用到return出來的參數時,兩種方式都可以。

fixture做后置處理

pytest中我們有teardown之類的后置處理,fixture也可以支持相關操作,通過yield關鍵字來實現。

比如示例:

 

@pytest.fixture(scope='module', autouse=True)

def test1():

    print('\n開始執行module')

    yield test1

# 因為scope='module',所以這個后置處理在該py模塊執行完后再執行,所以執行結果是在最后打印了這句話

    print('\ntest1的后置處理')    

 

@pytest.fixture(scope='class', autouse=True)

def test2():

    print('\n開始執行class')

    yield test2

# scope='class',所以在每次類運行后執行

    print('\ntest2的后置處理')   

 

@pytest.fixture(scope='function', autouse=True)

def test3():

    print('\n開始執行function')

    yield test3

# scope='function',所以在每個方法執行完畢后執行

    print('\ntest3的后置處理')
 

def test_a():

    print('---用例a執行---')


class TestCase:

    def test_b(self):

        print('---用例b執行---')

 

    def test_c(self):

        print('---用例c執行---')

 

執行結果:

開始執行module

開始執行class

開始執行function

---用例a執行---

.

test3的后置處理

test2的后置處理

開始執行class

開始執行function

---用例b執行---

.

test3的后置處理

開始執行function

---用例c執行---

.

test3的后置處理

test2的后置處理

test1的后置處理

示例二:

打開火狐瀏覽器,百度首頁搜索pytest,檢查是否搜索成功示例:

 

 

import pytest

from selenium import webdriver

import time

 

@pytest.fixture()

def fixtureFunc():

    '''實現瀏覽器的打開和關閉'''

    driver = webdriver.Firefox()

    # 類似return driver,將driver返回給調用者,不同的是被掉函數執行時遇到yield會停止執行,接着執行調用處的函數,調用處的函數執行完后會繼續執行yield關鍵后面的代碼

    yield driver

    driver.quit()


def test_search(fixtureFunc):

    '''訪問百度首頁,搜索pytest字符串是否在頁面源碼中'''

    driver = fixtureFunc

    driver.get('http://www.baidu.com')

    driver.find_element_by_id('kw').send_keys('pytest')

    driver.find_element_by_id('su').click()

    time.sleep(3)

    source = driver.page_source

    assert 'pytest' in source
 

if __name__ == '__main__':

    pytest.main(['-s', 'test_fixture_yield.py'])

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM