天天看點

pytest單側子產品_入門彙總

Pytest簡單介紹

(pytest是python的一個測試架構,主要是用來進行一些小的測試)

  • 安裝:pip install -U pytest
  • 檢視是否安裝成功:pytest --version
  • 運作:在目前檔案所在目錄下執行pytest,會尋找目前目錄以及子目錄下以test開頭的py檔案或者以test結尾的py檔案,找到檔案後,在檔案中找到以test開頭函數并執行。(或者執行pytest 檔案名--這樣可以指定某個檔案進行pytest的測試  或者  python -m pytest xxx.py)

在執行pytest xxx.py時,若增加參數:pytest -v -s xxx.py   (-v:顯示運作的函數;-s:顯示内部的列印資訊)

  • 編寫pytest測試樣例的規則:
  1. 測試檔案以test_開頭
  2. 測試類以Test開頭,并且不能帶有init方法
  3. 測試函數以test_開頭
  4. 斷言使用基本的assert即可
  • 斷言--正常:
assert value == 0      
  • 斷言--異常(pytest.raise方法):
1 #斷言1除以0,将産生一個ZeroDivisionError類型的異常。
 2 import pytest  
 3 def test_zero_division():  
 4     with pytest.raises(ZeroDivisionError):  
 5         1 / 0       
 6         
 7 #有的時候,我們可能需要在測試中用到産生的異常中的某些資訊,比如異常的類型type,異常的值value等等。下面我們修改下上面的測試:
 8 import pytest  
 9 def test_recursion_depth():  
10     with pytest.raises(ZeroDivisionError) as excinfo:  
11     1/0  
12     assert excinfo.type == 'RuntimeError'  
13 #因為該測試斷言産生的異常類型是RuntimeError,而實際上産生的異常類型是ZeroDivisionError,是以測試失敗了。在測試結果中,可以看到assert子表達式excinfo.type的值。      
  • 在test前,可以通過增加skip或xfail進行跳過(失敗)測試案例
1 import pytest
2 
3 # @pytest.mark.skipif()   跳過
4 # @pytest.mark.skip()    跳過
5 # @pytest.mark.xfail()     若出錯跳過,pass也跳過,但會顯示pass
6 def test_123():
7     # pytest.skip()    也可以實作'跳過'目的
8     assert      
  • 簡單例子:
1 # coding:utf-8
2 import pytest
3 
4 def fun(x):
5     return x + 1
6 
7 def test_fun():
8     assert      

結果:

(venvX) F:\test_jiaxin>pytest test_para1.py
============================= test session starts =============================
platform win32 -- Python 2.7.9, pytest-3.7.1, py-1.5.3, pluggy-0.7.1
rootdir: F:\test_jiaxin, inifile:
plugins: allure-adaptor-1.7.10
collected 1 item                                                               

test_para1.py .                                                          [100%]

========================== 1 passed in 0.09 seconds ===========================      

pytest之feature-scope

  • function:每個test都運作,預設是function的scope.
  • class:每個class的所有test隻運作一次.
  • module:每個module的所有test隻運作一次.
  • session:每個session隻運作一次.

比如你的所有test都需要連接配接同一個資料庫,那可以設定為module,隻需要連接配接一次資料庫,對于module内的所有test,這樣可以極大的提高運作效率。

代碼:

1 @pytest.fixture(scope="module")
2     def hero_backup_policy(self, acmp_cfg):
3         return AcloudBackupPolicy(acmp_cfg)
4 
5 @pytest.fixture(scope="function")
6     def hero_acloud_backup_policy(self, acmp_cfg):
7         return      

pytest的參數化方式

  • pytest.fixture()方式進行參數化,fixture裝飾的函數可以作為參數傳入其他函數
  • conftest.py 檔案中存放參數化函數,可作用于子產品内的所有測試用例
  • pytest.mark.parametrize()方式進行參數化

待測試代碼片段:(is_leap_year.py)

1 # coding:utf-8
 2 def is_leap_year(year):
 3     # 先判斷year是不是整型
 4     if isinstance(year, int) is not True:
 5         raise TypeError("傳入的參數不是整數")
 6     elif year == 0:
 7         raise ValueError("公元元年是從公元一年開始!!")
 8     elif abs(year) != year:
 9         raise ValueError("傳入的參數不是正整數")
10     elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
11         print"%d年是閏年" % year
12         return True
13     else:
14         print"%d年不是閏年" % year
15         return      

pytest.fixture()

  1. pytest.fixture()中傳入的參數為list,用例執行時,周遊list中的值,每傳入一次值,則相當于執行一次用例。
  2. @pytest.fixture()裝飾的函數中,傳入了一個參數為request。
  3. 這裡的測試資料是直接存在list中的,能否存入json檔案或者xml檔案再進行讀取轉換為list呢?

代碼:

1 # coding:utf-8
 2 import is_leap_year
 3 import pytest
 4 
 5 class TestPara():
 6     is_leap = [4, 40, 400, 800, 1996, 2996]
 7     is_not_leap = [1, 100, 500, 1000, 1999, 3000]
 8     is_valueerror = [0, -4, -100, -400, -1996, -2000]
 9     is_typeerror = ['-4', '4', '100', 'ins', '**', '中文']
10 
11     @pytest.fixture(params=is_leap)
12     def is_leap(self, request):
13         return request.param
14 
15     @pytest.fixture(params=is_typeerror)
16     def is_typeerror(self, request):
17         return request.param
18 
19     def test_is_leap(self, is_leap):
20         assert is_leap_year.is_leap_year(is_leap) == True
21 
22     def test_is_typeerror(self, is_typeerror):
23         with pytest.raises(TypeError):
24      

conftest.py 檔案 -- 測試資料與用例分離

  1. 采用conftest.py檔案存儲參數化資料和函數,子產品下的用例執行時,會自動讀取conftest.py檔案中的資料。

代碼:

conftest.py檔案
 1 # coding:utf-8
 2 import pytest
 3 
 4 is_leap = [4, 40, 400, 800, 1996, 2996]
 5 is_not_leap = [1, 100, 500, 1000, 1999, 3000]
 6 is_valueerror = [0, -4, -100, -400, -1996, -2000]
 7 is_typeerror = ['-4', '4', '100', 'ins', '**', '中文']
 8 
 9 @pytest.fixture(params=is_leap)
10 def is_leap(request):
11     return request.param
12 
13 @pytest.fixture(params=is_typeerror)
14 def is_typeerror(request):
15     return      
test_para.py檔案
 1 # coding:utf-8
 2 
 3 import is_leap_year
 4 import pytest
 5 
 6 class TestPara():
 7     def test_is_leap(self, is_leap):
 8         assert is_leap_year.is_leap_year(is_leap) == True
 9 
10     def test_is_typeerror(self, is_typeerror):
11         with pytest.raises(TypeError):
12      

pytest.mark.parametrize() -- 進行參數化

  1. 采用标記函數參數化,傳入單個參數,pytest.mark.parametrize("參數名",lists)。
  2. 采用标記函數傳入多個參數,如pytest.mark.parametrize("para1, para2", [(p1_data_0, p2_data_0), (p1_data_1, p2_data_1),...]。
  3. 這裡:測試用例中傳入2個參數,year和期望結果,使輸入資料與預期結果對應,構造了2組會失敗的資料,在執行結果中,可以看到失敗原因。

代碼:

1 # coding:utf-8
 2 import is_leap_year
 3 import pytest
 4 
 5 class TestPara():
 6     # 參數傳入year中
 7     @pytest.mark.parametrize('year, expected', [(1, False), (4,True), (100, False), (400, True), (500, True)])
 8     def test_is_leap(self, year, expected):
 9         assert is_leap_year.is_leap_year(year) == expected
10 
11     @pytest.mark.parametrize('year, expected', [(0, ValueError),('-4', TypeError), (-4, ValueError), ('ss', TypeError), (100.0, ValueError)])
12     def test_is_typeerror(self, year, expected):
13         if expected == ValueError:
14             with pytest.raises(ValueError) as excinfo:
15                 is_leap_year.is_leap_year(year)
16                 assert excinfo.type == expected
17         else:
18             with pytest.raises(TypeError) as excinfo:
19                 is_leap_year.is_leap_year(year)
20                 assert      

pytest-fixture擴充内容

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

1 import pytest
 2 
 3 @pytest.fixture()
 4 def before():
 5     print '\nbefore each test'
 6 
 7 def test_1(before):
 8     print 'test_1()'
 9 
10 def test_2(before):
11     print 'test_2()'      

result:

(venvX) F:\test_jiaxin>pytest -v -s test_compute.py
============================= test session starts =============================
platform win32 -- Python 2.7.9, pytest-3.7.1, py-1.5.3, pluggy-0.7.1 -- f:\projects\sandom\venvx\scripts\python.exe
cachedir: .pytest_cache
rootdir: F:\test_jiaxin, inifile:
plugins: allure-adaptor-1.7.10
collected 2 items                                                              

test_compute.py::test_1
before each test
test_1()
PASSED
test_compute.py::test_2
before each test
test_2()
PASSED

========================== 2 passed in 0.17 seconds ===========================      

2.  進行封裝

1 import pytest
 2 
 3 @pytest.fixture()
 4 def before():
 5     print '\nbefore each test'
 6 
 7 # 每個函數前聲明
 8 @pytest.mark.usefixtures("before")
 9 def test_1():
10     print 'test_1()'
11 @pytest.mark.usefixtures("before")
12 def test_2():
13     print 'test_2()'
14 
15 #封裝在類裡,類裡的每個成員函數聲明
16 class Test1:
17     @pytest.mark.usefixtures("before")
18     def test_3(self):
19         print 'test_1()'
20     @pytest.mark.usefixtures("before")
21     def test_4(self):
22         print 'test_2()'
23 
24 #封裝在類裡在前聲明
25 @pytest.mark.usefixtures("before")
26 class Test2:
27     def test_5(self):
28         print 'test_1()'
29     def test_6(self):
30         print 'test_2()'      

result:

(venvX) F:\test_jiaxin>pytest -v -s test_compute.py
============================= test session starts =============================
platform win32 -- Python 2.7.9, pytest-3.7.1, py-1.5.3, pluggy-0.7.1 -- f:\projects\sandom\venvx\scripts\python.exe
cachedir: .pytest_cache
rootdir: F:\test_jiaxin, inifile:
plugins: allure-adaptor-1.7.10
collected 6 items                                                              

test_compute.py::test_1
before each test
test_1()
PASSED
test_compute.py::test_2
before each test
test_2()
PASSED
test_compute.py::Test1::test_3
before each test
test_1()
PASSED
test_compute.py::Test1::test_4
before each test
test_2()
PASSED
test_compute.py::Test2::test_5
before each test
test_1()
PASSED
test_compute.py::Test2::test_6
before each test
test_2()
PASSED

========================== 6 passed in 0.11 seconds ===========================      

3.  fixture還可以帶參數,可以把參數指派給params,預設是None。對于param裡面的每個值,fixture都會去調用執行一次,就像執行for循環一樣把params裡的值周遊一次。

1 import pytest
2 
3 @pytest.fixture(params=[1, 2, 3])
4 def test_data(request):
5     return request.param
6 
7 def test_not_2(test_data):
8     print('test_data: %s' % test_data)
9     assert      

result:

(venvX) F:\test_jiaxin>pytest -v -s test_compute.py
============================= test session starts =============================
platform win32 -- Python 2.7.9, pytest-3.7.1, py-1.5.3, pluggy-0.7.1 -- f:\projects\sandom\venvx\scripts\python.exe
cachedir: .pytest_cache
rootdir: F:\test_jiaxin, inifile:
plugins: allure-adaptor-1.7.10
collected 3 items                                                              

test_compute.py::test_not_2[1] test_data: 1
PASSED
test_compute.py::test_not_2[2] test_data: 2
FAILED
test_compute.py::test_not_2[3] test_data: 3
PASSED

================================== FAILURES ===================================
________________________________ test_not_2[2] ________________________________

test_data = 2

    def test_not_2(test_data):
        print('test_data: %s' % test_data)
>       assert test_data != 2
E       assert 2 != 2

test_compute.py:64: AssertionError
===================== 1 failed, 2 passed in 0.12 seconds ======================