Pytest 順序執行,依賴執行,引數化執行

simonpatrick發表於2024-05-14

Pytest 順序執行,依賴執行,引數化執行

進行下面實驗前,需要安裝 python 和 pytest,我一般用 poetry 來管理,如何建立就是以下幾個命令:

poetry init 
poetry add pytest --group test
  • Pytest 順序執行,依賴執行,引數化執行
    • 0- 被測物件
    • 1. 使用 pytest 進行測試這個服務的常見場景和解法
    • 1.1 pytest-單獨測試一個方法
    • 1.2 Pytest-測試方法 B 但是需要先執行測試方法 A
    • 1.3 pytest - 引數化執行
    • 2. 使用的一些小結 <!-- TOC -->

0- 被測物件

假設被測試物件就是一個計算加減乘除的一個服務,這裡把一個方法就等同於一個 API 介面,實際上也確實沒有
太大區別,一個是本地呼叫,一個是遠端呼叫,遠端呼叫需要一個客戶端,本地呼叫直接就用這個類呼叫了

"""
service functions for calculation
"""


def add(a, b):
    return a + b


def subtract(a, b):
    return a - b


def multiply(a, b):
    return a * b


def divide(a, b):
    return a * 1.0 / b

1. 使用 pytest 進行測試這個服務的常見場景和解法

  1. 單獨測試一個方法
  2. 測試方法 B 但是需要先執行測試方法 A
  3. 引數化測試,引數可能是靜態寫死的,也可能是動態的

1.1 pytest-單獨測試一個方法

使用如下方法就可以進行單獨的方法測試,基本原則就是: 一個方法一個測試,測試資料都固定

import allure
import pytest

from app.module_a.service import subtract, add


@allure.feature("減法結果為正")
def test_subtract_positive():
    result = subtract(2, 1)
    assert result == 1


@allure.feature("減法結果為為負數")
def test_subtract_negative():
    result = subtract(1, 2)
    assert result == -1

1.2 Pytest-測試方法 B 但是需要先執行測試方法 A

如果遇到: 測試方法 B 但是需要先執行測試方法 A,這裡面有一些小問題就是:

  1. 順序不能絕對保證: 如果透過執行 pytest 的命令列時候,有可能不能確保測試 A 一定跑在測試 B 前面
  2. 測試可讀性問題: s 不仔細看程式碼,不能看出來 - 測試方法 B 但是需要先執行測試方法 A

這個問題可以透過 pytest 的外掛 pytest-order 來解決:

poetry add pytest-order

測試程式碼如下: 透過@pytest.mark.order定於顯示的定於執行順序,好處有兩個:

  1. 確保順序
  2. 提高測試可讀性,一眼就看出來那個先跑那個後跑
  3. 不好的地方是: 單獨執行第二個測試: test_subtract_order 會報錯,但是因為寫了 order 標識, 大概一下子也能明白怎麼回事
context = {}


@pytest.mark.order(1)
def test_add_positive():
    result = add(1, 2)
    assert result == 3
    context['USED_FOR_SUBTRACT'] = result


@allure.feature("被減數是加法函式計算出來的結果")
@pytest.mark.order(2)
def test_subtract_order():
    result = subtract(1, context['USED_FOR_SUBTRACT'])
    assert result == -2

如果使用 pytest-dependency 外掛解決,程式碼也類似,就不多說明了:

context = {}

@pytest.mark.order(1)
@user8cy()
def test_add_positive():
    result = add(1, 2)
    assert result == 3
    context['USED_FOR_SUBTRACT'] = result

@user9cy(depends=["test_add_positive"])
def test_subtract_dep():
    result = subtract(1, context['USED_FOR_SUBTRACT'])
    assert result == -2

這裡面注意一點: 用來存資料的用字典會好那麼一點,為了避免一些想不到的麻煩我自己就一直用字典了,也沒有用 global 這樣的東西。

1.3 pytest - 引數化執行

  • 有時很多 case 都很類似,只要給資料自動跑就行了,不想寫很多單獨的測試方法了,就用引數化執行, 最簡單的就是直接給上固定的資料, 以下程式碼一看就明白
fixed_params = [(1, 2, 3), (-1, 1, 0)]


@user10ize('a,b,expected', fixed_params)
def test_add_parameterized(a, b, expected):
    actual = add(a, b)
    assert actual == expected
  • 有時呢,引數化的時候有一兩個數字是需要動態獲得以下的,那麼可以用以下方式簡單處理以下, 就是寫一個函式區構建這些引數化的測試資料
def dynamic_cases():
    d_params = [(1, 2, 3), (-1, 1, 0)]
    dynamic_case = (1, add(1, 2), 4)
    d_params.append(dynamic_case)
    return d_params


@user11ize('a,b,expected', dynamic_cases())
def test_add_parameterized(a, b, expected):
    actual = add(a, b)
    assert actual == expected

2. 使用的一些小結

有時可能更復雜,那麼其實呢,我自己的做法就是:

  1. 混合使用順序 order/依賴空 dependency 和引數化化進行處理
  2. 能引數化的和複雜的 case 分開,比如: A. 一些異常錯誤資料,出錯資訊的測試,這些引數化可能容易一些就引數化了 B. 複雜場景的,計算,拿前一個返回再構建後一個的人參之類的,乾脆就單獨寫一個測試方法了
  3. 不去想一個統一的解法,寫程式碼和在測試平臺上寫測試用例的最大的區別就是:測試平臺一般想用一種方法解決所有問題, 而自己寫程式碼的壞,就是完全自己控制靈活度, 各有好壞,既然自己寫程式碼了, 那就多多利用寫程式碼的靈活度解決自己的問題,有時找一個可以適用所有的方法可能很麻煩,不如直接手寫一個測試方法來的簡單直接 還節約一點時間。

相關文章