Python測試框架 - Pytest

一 . Pytest 簡介

Pytest是python的一種單元測試框架。

1. pytest 特點
  • 入門簡單,文檔豐富
  • 支持單元測試,功能測試
  • 支持參數(shù)化,重復執(zhí)行,部分執(zhí)行,測試跳過
  • 兼容其他測試框架(nose,unittest 等)
  • 支持生成html報告
  • 可集成CI環(huán)境(Jenkins 等)
  • 第三方插件豐富,良好的自定義擴展性
2. pytest 與 unittest

(1)unittest

  • 測試文件必須先 import unittest
  • 測試類必須繼承unittest.TestCase
  • 測試方法必須以“test_”開頭
  • 測試類必須要有unittest.main()方法
  • unittest只有setup/teardown裝載測試用例
  • ...

(2)pytest

  • 測試文件名必須以“test_”開頭
  • 測試類以Test開頭,并且不能帶有 init 方法
  • 測試方法必須以“test_”開頭
  • 除了有setup/teardown,還能更自由的定義fixture裝載測試用例
  • ...

(3)Comparison Table

Pytest vs Unittest


二 . Pytest 安裝

# 安裝
pip install -U pytest

# 查看安裝版本
pip show pytest

# 或者
pytest --version

三 . Pytest 運行

一個簡單的例子 test_class.py:

# test_class.py
class TestClass:
    def test_one(self):
        x = "this"
        assert 'h' in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, 'check')
1. cmd 中運行測試用例
# cmd中 cd到 test_class.py所在文件夾
# 運行 該文件夾中所有測試用例
pytest
# 或者
py.test

# 運行指定測試用例,加上-q參數(shù)用來指定執(zhí)行的文件
pytest -q test_class.py

pytest運行規(guī)則:
查找當前目錄及其子目錄下以test_.py或_test.py文件,
找到文件后,在文件中找到以test開頭函數(shù)并執(zhí)行。

2. pycharm 中運行測試用例

一個簡單的例子 test_sample.py:

# content of test_sample.py
import pytest

def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5

if __name__ == "__main__":
    pytest.main('-q test_sample.py')

pycharm中運行pytest:

  • file->Setting->Tools->Python Integrated Tools->項目名稱->Default test runner->選擇py.test
  • 右鍵選擇pytest運行或者直接運行.py文件

四 . Pytest 裝飾器

1. setup 與 teardown

裝載運行級別:

  • 模塊級(setup_module/teardown_module)開始于模塊始末
  • 類級(setup_class/teardown_class)只在類中前后運行一次(在類中)
  • 方法級(setup_method/teardown_method)開始于方法始末(在類中)
  • 類里面的(setup/teardown)運行在調(diào)用方法的前后
  • 函數(shù)級(setup_function/teardown_function)只對函數(shù)用例生效(不在類中)

一個簡單的例子 test_module.py:

# -*- coding: utf-8 -*-
# Version : V1.0
# Author  : Seven
# Date    : 2018/8/2 19:41

import pytest


# 模塊中的方法
def setup_module():
    print("setup_module:整個.py模塊只執(zhí)行一次")


def teardown_module():
    print("teardown_module:整個test_module.py模塊只執(zhí)行一次")


def setup_function():
    print("setup_function:每個用例開始前都會執(zhí)行")


def teardown_function():
    print("teardown_function:每個用例結(jié)束后都會執(zhí)行")


# 測試模塊中的用例1
def test_one():
    print("正在執(zhí)行測試模塊----test_one")
    x = "this"
    assert 'h' in x


# 測試模塊中的用例2
def test_two():
    print("正在執(zhí)行測試模塊----test_two")
    x = "hello"
    assert hasattr(x, 'check')


# 測試類
class TestCase():

    def setup_class(self):
        print("setup_class:所有用例執(zhí)行之前")

    def teardown_class(self):
        print("teardown_class:所有用例執(zhí)行之后")

    def setup(self):
        print("setup:每個用例開始前都會執(zhí)行")

    def teardown(self):
        print("teardown:每個用例結(jié)束后都會執(zhí)行")

    def test_three(self):
        print("正在執(zhí)行測試類----test_three")
        x = "this"
        assert 'h' in x

    def test_four(self):
        print("正在執(zhí)行測試類----test_four")
        x = "hello"
        assert hasattr(x, 'check')


if __name__ == "__main__":
    pytest.main(["-s", "test_module.py"])

運行的效果:

collected 4 items                                                              

test_module.py setup_module:整個.py模塊只執(zhí)行一次
setup_function:每個用例開始前都會執(zhí)行
正在執(zhí)行測試模塊----test_one
.teardown_function:每個用例結(jié)束后都會執(zhí)行
setup_function:每個用例開始前都會執(zhí)行
正在執(zhí)行測試模塊----test_two
Fteardown_function:每個用例結(jié)束后都會執(zhí)行
setup_class:所有用例執(zhí)行之前
setup:每個用例開始前都會執(zhí)行
正在執(zhí)行測試類----test_three
.teardown:每個用例結(jié)束后都會執(zhí)行
setup:每個用例開始前都會執(zhí)行
正在執(zhí)行測試類----test_four
Fteardown:每個用例結(jié)束后都會執(zhí)行
teardown_class:所有用例執(zhí)行之后
teardown_module:整個test_module.py模塊只執(zhí)行一次

從結(jié)果看出:

  • setup_module/teardown_module的優(yōu)先級是最大的
  • 然后函數(shù)里面的setup_function/teardown_function與類里面的setup_class/teardown_class互不干涉
2. fixture

一個簡單的例子 test_fixture.py:

# conftest.py
# -*- coding: utf-8 -*-
import pytest


@pytest.fixture(scope="function")
def login():
    print("請先輸入賬號和密碼,然后登陸")

    yield
    print("退出登陸")
# test_1.py
# -*- coding: utf-8 -*-
import pytest


def test_fix1(login):
    print("test_fix1 in test_1.py:需要登陸再執(zhí)行操作")


def test_fix2():
    print("test_fix2 in test_1.py:不需要登陸再執(zhí)行操作")


def test_fix3(login):
    print("test_fix3 in test_1.py:需要登陸再執(zhí)行操作")


if __name__ == "__main__":
    pytest.main(['-s', 'test_1.py'])
# test_2.py
# -*- coding: utf-8 -*-
import pytest


def test_fix3():
    print("test_fix3 in test_2.py:不需要登陸再執(zhí)行操作")


def test_fix4(login):
    print("test_fix4 in test_2.py:需要登陸再執(zhí)行操作")


if __name__ == "__main__":
    pytest.main(['-s', 'test_2.py'])

文件結(jié)構(gòu):

conftest configuration

運行效果:

pytest -s test_1.py

collected 3 items                                                              

test_1.py 請先輸入賬號和密碼,然后登陸
test_fix1 in test_1.py:需要登陸再執(zhí)行操作
.退出登陸
test_fix2 in test_1.py:不需要登陸再執(zhí)行操作
.請先輸入賬號和密碼,然后登陸
test_fix3 in test_1.py:需要登陸再執(zhí)行操作
.退出登陸
pytest -s test_2.py

collected 2 items                                                              

test_2.py test_fix3 in test_2.py:不需要登陸再執(zhí)行操作
.請先輸入賬號和密碼,然后登陸
test_fix4 in test_2.py:需要登陸再執(zhí)行操作
.退出登陸

從結(jié)果看出:
(1) scope 參數(shù) 實現(xiàn)控制 setup 級別
(2) conftest.py 配置 實現(xiàn)跨文件調(diào)用裝飾器
(3) yield 實現(xiàn) teardown


五 . Pytest 斷言

  • pytest允許使用python的標準assert語句進行斷言處理

六 . Pytest 自動生成報告

# 生成xml格式的報告
pytest -v test_1.py --junitxml=Path

# 生成txt格式的報告
pytest -v test_1.py --resultlog=Path

# 生成html格式的報告
# 需預先裝上pytest-html
pip install pytest_html
pytest -v test_1.py --html=Path

生成報告效果圖:

pytest_report


七. 參考

  1. https://docs.pytest.org/en/latest/
  2. https://mp.weixin.qq.com/s/OSVEhsMIzLBY04Nz-sbrPg
  3. https://github.com/renzon/pytest-vs-unittest
  4. https://blog.csdn.net/HeatDeath/article/details/79733527
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容