[TOC]
>[info] 學習了一段時間的python入門基礎了,大家應該已經具備開發接口測試代碼的能力了。進行接口測試,我們還需要掌握一種測試框架,來組織我們的用例執行,用例斷言。下面我們介紹一款最666的測試框架`pytest`
## pytest 介紹
pytest是python的一種單元測試框架,與python自帶的unittest測試框架類似,但是比unittest框架使用起來更簡潔,效率更高。根據pytest的官方網站介紹,它具有如下特點:
* 非常容易上手,入門簡單,文檔豐富,文檔中有很多實例可以參考
* 能夠支持簡單的單元測試和復雜的功能測試
* 支持參數化
* 執行測試過程中可以將某些測試跳過,或者對某些預期失敗的case標記成失敗
* 支持重復執行失敗的case
* 支持運行由nose, unittest編寫的測試case
* 具有很多第三方插件,并且可以自定義擴展
* 方便的和持續集成工具集成
## pytest 安裝
```cmd
pip install -U pytest
```
## pytest 的使用
### Pytest測試樣例的命名規則
1. 測試文件以test_開頭或結尾(否則用py.test命令行不能自動識別)
2. 測試類以Test開頭,且不能帶有init方法
3. 測試函數以test_開頭
4. 斷言使用assert
5. fixture的文件名必須是`conftest.py`
### 創建一個最簡單的例子
test_sample.py
```python
def func(x):
return x + 1
def test_answer_right():
assert func(3) == 4
def test_answer_wrong():
assert func(3) == 5
```
執行
```cmd
pytest test_sample.py
```
運行結果如下:
```cmd
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: E:\workspace\python_leaning, inifile:
collected 2 items
test_sample.py .F
test_sample.py:12 (test_answer_wrong)
def test_answer_wrong():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:14: AssertionError
[100%]
================================== FAILURES ===================================
______________________________ test_answer_wrong ______________________________
def test_answer_wrong():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:14: AssertionError
===================== 1 failed, 1 passed in 0.09 seconds ======================
```
可以發現,執行了兩個用例,一個成功,一個失敗。
上述命令可以使pytest自動查找 test_sample.py 文件下所有的格式為:`test_*`的方法,然后執行用例.執行順序為方法的順序.
如果有多個py文件,可以直接使用下面命令
```
pytest
```
這個命令會找出當前目錄下所有格式為`test_*.py `或 `*_test.py` 的文件
然后執行用例(文件內格式為`test_*`的方法)
### 在類中分組組織用例
test_sample.py
```python
import pytest
class TestSample(object):
@pytest.fixture()
def count(self):
print('init count')
return 10
def test_answer_1(self, count):
print('test_answer_1 get count: %s' % count)
assert count == 10
def test_answer_2(self, count):
print('test_answer_2 get count: %s' % count)
assert count == 10
```
運行命令
```
pytest -s -q
```
`-s`: 是為了輸出打印日志,如果不輸入-s,是沒有日志輸出的。
`-q`:quiet 報道模式,這樣輸出會好看點
運行結果如
```cmd
init count
test_answer_1 get count: 10
.init count
test_answer_2 get count: 10
.
2 passed in 0.05 seconds
```
## fixtures
說到測試框架自然要說到setup和teardown兩個方法。
* setup:是用來做準備操作.一般用來初始化資源.
* teardown:是用來做收尾操作.一般用了關閉資源.
pytest的setup和teardown是利用`@pytest.fixture`這個注釋來完成的.不僅可以完成初始化操作,初始化后如果有數據需要傳給用例使用也是非常方便!
通過`@pytest.fixture() `注釋會在執行測試用例之前初始化操作.然后直接在測試用例的方法中就可以拿到初始化返回的參數(<span style="color:red;">參數名</span>要和<span style="color:red;">初始化的方法名</span>**一樣**)
在這個實例中,初始化的方法名為`count`,初始化后返回 10。當在測試用例方法中傳入與初始化方法名同為`count`的參數時,相對于傳入參數`count=10`
### 在fixtures中控制setup與teardown
test_sample.py:
```pyhon
import pytest
class TestSample(object):
@pytest.fixture()
def count(self,request):
print("init count")
def fin():
print("teardown count")
request.addfinalizer(fin) # teardown
return 10 # provide the fixture value
def test_answer_1(self, count):
print('test_answer_1 get count: %s' % count)
assert count == 10
def test_answer_2(self, count):
print('test_answer_2 get count: %s' % count)
assert count == 10
```
運行命令 ``` pytest -s -q```
```cmd
init count
test_answer_1 get count: 10
.teardown count
init count
test_answer_2 get count: 10
.teardown count
```
在fixture方法中, request.addfinalizer(),會將某函數作為teardown,在用例執行之后執行。
### fixture 的參數scope
`fixture` 有一個域(`scope`)的概念,用來指定該 `fixture` 的使用范圍.
它有五種作用域,如`session`/`module`/`class`/`function`(default).
- function:每個測試函數之前執行一次
- class:每個測試類之前執行一次
- module:每個module之前執行一次
- session:每次session之前執行一次,即每次測試執行一次
觀察上面例子的運行結果,可以發現`init count`與`teardown count` 都打印了兩次,那是因為`fixture`默認域是`function`,即每執行一個用例方法前,都會執行初始化方法一次。
為了演示`fixture`的作用域,這里修改一下工程的目錄結構,將`fixture`放到一個名稱為`conftest.py`(這是pytest規定的命名)的文件中.
>[info] 如果一個fixture需要共享,不用在每個py文件中寫一遍,寫在conftest.py文件中就好。
`conftest.py`
```python
import pytest
@pytest.fixture(scope="session")
def count():
print("init count")
yield 10
print("teardown count")
```
`test_sample.py`:
```python
class TestSample(object):
def test_answer_1(self, count):
print('test_answer_1 get count: %s' % count)
assert count == 10
def test_answer_2(self, count):
print('test_answer_2 get count: %s' % count)
assert count == 10
```
運行命令 ``` pytest -s -q```
```cmd
init count
test_answer_1 get count: 10
.test_answer_2 get count: 10
.teardown count
2 passed in 0.06 seconds
```
這是在使用 seesion 域的情況下,可以發現`init count` 與 `teardown` 只執行一次.假如我們需要這種情況,每一個類都執行一次初始化方法,那么很簡單直接把域改成 class 即可,同理 module 是基于模塊的.
### fixture 的參數params
在`test_sample.py`中添加一個帶params的fixture,并且在用例中調用這個fixture
```python
import pytest
@pytest.fixture(params=[1, 2, 3])
def test_data(request):
return request.param
class TestSample(object):
@pytest.mark.skip()
def test_answer_1(self, count):
print('test_answer_1 get count: %s' % count)
assert count == 10
@pytest.mark.skip()
def test_answer_2(self, count):
print('test_answer_2 get count: %s' % count)
assert count == 10
def test_answer_3(self, test_data):
print('test_data: %s' % test_data)
assert test_data in [1, 2, 3]
```
執行命令```pytest -s -q```
```cmd
test_data: 1
.test_data: 2
.test_data: 3
.
3 passed, 2 skipped in 0.05 seconds
```
這里通過`@pytest.mark.skip()`跳過了一些不想要執行的用例
通過執行結果可知,fixture還可以帶參數,把參數賦值給params,默認是None。對于param里面的每個值,fixture都會去調用執行一次,就像執行for循環一樣把params里的值遍歷一次。
## 用例運行順序 pytest-ordering
pytest按照在.py中的出現位置(`從上到下`)執行用例,可以通過`pytest-ordering` 插件來改變執行順序
### 安裝pytest-ordering
```cmd
pip install pytest-ordering
```
### 在用例前,使用裝飾器 `@pytest.mark.run` 指定用例順序
當用例中存在 `@pytest.mark.run` 時,先按照order從小到大執行,然后再從上往下執行。
```python
import pytest
@pytest.fixture(params=[1, ])
def test_data(request):
return request.param
class TestSample(object):
@pytest.mark.run(order=2)
def test_answer_1(self, count):
print('test_answer_1 get count: %s' % count)
assert count == 10
@pytest.mark.run(order=1)
def test_answer_2(self, count):
print('test_answer_2 get count: %s' % count)
assert count == 10
def test_answer_3(self, test_data):
print('test_data: %s' % test_data)
assert test_data in [1, 2, 3]
```
執行命令 ```pytest -s -q```
```cmd
init count
test_answer_2 get count: 10
.test_answer_1 get count: 10
.test_data: 1
.teardown count
3 passed in 0.09 seconds
```
## pytest生成html測試報告 pytest-html
pytest-html是py.test的一個插件,它為測試結果生成一個HTML報告。
### 安裝pytest-html
```cmd
pip install pytest-html `
```
### 運行測試用例
```cmd
pytest --html=report.html
```
運行后,在當前目錄下生產`report.html`,如下

## 在代碼中執行用例
有些時候需要在代碼中調用執行case的方法.而不是通過command的方式.我們可以在python代碼中加入下面代碼即可:
```python
args = ['-s', '-q']
pytest.main(args)
```
## 參考文檔
作為pytest新手入門,先快速掌握如上部分內容,更多有用的pytest內容,后續會繼續更新。
官方文檔: [https://docs.pytest.org/en/latest/contents.html](https://docs.pytest.org/en/latest/contents.html)
<hr style="margin-top:100px">
:-: 
***微信掃一掃,關注“python測試開發圈”,了解更多測試教程!***
- 前言
- chapter01_開發環境
- chapter02_字符串的使用
- chapter03_列表的使用
- chapter04_字典的使用
- chapter05_數字的使用
- chapter06_元組的使用
- chapter07_集合的使用
- chapter08_輸入輸出
- chapter09_控制流程
- chapter10_實例練習_登錄1
- chapter11_python函數入門
- chapter12_python中的類
- chapter13_輕松玩轉python中的模塊管理
- chapter14_掌握學習新模塊的技巧
- chapter15_通過os模塊與操作系統交互
- chapter16_子進程相關模塊(subprocess)
- chapter17_時間相關模塊(time & datetime)
- chapter18_序列化模塊(json)
- chapter19_加密模塊(hashlib)
- chapter20_文件的讀與寫
- chapter21_階段考核2_登錄
- chapter22_小小算法挑戰(排序&二分法)
- chapter23_用多線程來搞事!
- chapter24_HTTP接口請求(requests)
- chapter25_接口測試框架(pytest)
- chapter26_階段考核3_HTTP接口測試
- chapter27_HTML解析(pyquery)
- chapter28_階段考核4_爬蟲下載網易汽車
- chapter29_python中的那些編碼坑
- chapter30_MySQL數據庫操作
- chapter31 高級特性_迭代器與生成器
- chapter32 高級特性_裝飾器
- chapter33 高級特性_列表處理