1. 程式人生 > >pytest封神之路第三步 精通fixture

pytest封神之路第三步 精通fixture

首先放一句“狠話”。 如果你不會fixture,那麼你最好別說自己會pytest。 (只是為了烘托主題哈,手上的磚頭可以放下了,手動滑稽) # fixture是什麼 看看原始碼 ```python def fixture( callable_or_scope=None, *args, scope="function", params=None, autouse=False, ids=None, name=None ): """Decorator to mark a fixture factory function. This decorator can be used, with or without parameters, to define a fixture function. The name of the fixture function can later be referenced to cause its invocation ahead of running tests: test modules or classes can use the ``pytest.mark.usefixtures(fixturename)`` marker. Test functions can directly use fixture names as input arguments in which case the fixture instance returned from the fixture function will be injected. Fixtures can provide their values to test functions using ``return`` or ``yield`` statements. When using ``yield`` the code block after the ``yield`` statement is executed as teardown code regardless of the test outcome, and must yield exactly once. :arg scope: the scope for which this fixture is shared, one of ``"function"`` (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"`` (``"package"`` is considered **experimental** at this time). This parameter may also be a callable which receives ``(fixture_name, config)`` as parameters, and must return a ``str`` with one of the values mentioned above. See :ref:`dynamic scope` in the docs for more information. :arg params: an optional list of parameters which will cause multiple invocations of the fixture function and all of the tests using it. The current parameter is available in ``request.param``. :arg autouse: if True, the fixture func is activated for all tests that can see it. If False (the default) then an explicit reference is needed to activate the fixture. :arg ids: list of string ids each corresponding to the params so that they are part of the test id. If no ids are provided they will be generated automatically from the params. :arg name: the name of the fixture. This defaults to the name of the decorated function. If a fixture is used in the same module in which it is defined, the function name of the fixture will be shadowed by the function arg that requests the fixture; one way to resolve this is to name the decorated function ``fixture_`` and then use ``@pytest.fixture(name='')``. """ if params is not None: params = list(params) fixture_function, arguments = _parse_fixture_args( callable_or_scope, *args, scope=scope, params=params, autouse=autouse, ids=ids, name=name, ) scope = arguments.get("scope") params = arguments.get("params") autouse = arguments.get("autouse") ids = arguments.get("ids") name = arguments.get("name") if fixture_function and params is None and autouse is False: # direct decoration return FixtureFunctionMarker(scope, params, autouse, name=name)( fixture_function ) return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) ``` 總結一下 【定義】 - fixture是一個函式,在函式上添加註解`@pytest.fixture`來定義 - 定義在conftest.py中,無需import就可以呼叫 - 定義在其他檔案中,import後也可以呼叫 - 定義在相同檔案中,直接呼叫 【使用】 - 第一種使用方式是`@pytest.mark.usefixtures(fixturename)`(如果修飾TestClass能對類中所有方法生效) - 第二種使用方式是作為函式引數 - 第三種使用方式是autouse(不需要顯示呼叫,自動執行) # conftest.py 我們常常會把fixture定義到conftest.py檔案中。 這是pytest固定的檔名,不能自定義。 必須放在package下,也就是目錄中有\_\_init\_\_.py。 conftest.py中的fixture可以用在當前目錄及其子目錄,不需要import,pytest會自動找。 可以建立多個conftest.py檔案,同名fixture查詢時會優先用最近的。 # 依賴注入 fixture實現了依賴注入。依賴注入是控制反轉(IoC, Inversion of Control)的一種技術形式。 簡單理解一下什麼是依賴注入和控制反轉
實在是妙啊!我們可以在不修改當前函式程式碼邏輯的情況下,通過fixture來額外新增一些處理。 # 入門示例 ```python # content of ./test_smtpsimple.py import smtplib import pytest @pytest.fixture def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert 0 # for demo purposes ``` 執行後程序處理邏輯 1. pytest找到test_開頭的函式,發現需要名字為smtp_connection的fixture,就去找 2. 找到之後,呼叫smtp_connection(),return了SMTP的例項 3. 呼叫test_ehlo() ,入參`smtp_connection`等於fixture return的值 如果想看檔案定義了哪些fixture,可以使用命令,\_字首的需要跟上-v ```shell pytest --fixtures test_simplefactory.py ``` # fixture scope & order 既然到處都可以定義fixture,那多了豈不就亂了? pytest規定了fxture的執行範圍和執行順序。 fixture的**範圍**通過引數scope來指定 ```python @pytest.fixture(scope="module") ``` 預設是function,可以選擇function, class, module, package 或 session。 fixture都是在test第一次呼叫時建立,根據scope的不同有不同的執行和銷燬方式 - function 每個函式執行一次,函式結束時銷燬 - class 每個類執行一次,類結束時銷燬 - module 每個模組執行一次,模組結束時銷燬 - package 每個包執行一次,包結束時銷燬 - session 每個會話執行一次,會話結束時銷燬 fixture的**順序**優先按scope從大到小,session > package > module > class > function。 如果scope相同,就按test呼叫先後順序,以及fixture之間的依賴關係。 autouse的fixture會優先於相同scope的其他fixture。 示例 ```python import pytest # fixtures documentation order example order = [] @pytest.fixture(scope="session") def s1(): order.append("s1") @pytest.fixture(scope="module") def m1(): order.append("m1") @pytest.fixture def f1(f3): order.append("f1") @pytest.fixture def f3(): order.append("f3") @pytest.fixture(autouse=True) def a1(): order.append("a1") @pytest.fixture def f2(): order.append("f2") def test_order(f1, m1, f2, s1): assert order == ["s1", "m1", "a1", "f3", "f1", "f2"] ``` 雖然test_order()是按f1, m1, f2, s1呼叫的,但是結果卻不是按這個順序 1. s1 scope為session 2. m1 scope為module 3. a1 autouse,預設function,後於session、module,先於function其他fixture 4. f3 被f1依賴 5. f1 test_order()引數列表第1個 6. f2 test_order()引數列表第3個 # fixture巢狀 fixture裝飾的是函式,那函式也有入參咯。 **fixture裝飾的函式入參,只能是其他fixture。** 示例,f1依賴f3,如果不定義f3的話,執行會報錯fixture 'f3' not found ```python @pytest.fixture def f1(f3): order.append("f1") @pytest.fixture def f3(): order.append("f3") def test_order(f1): pass ``` # 從test傳值給fixture 藉助request,可以把test中的值傳遞給fixture。 示例1,smtp_connection可以使用module中的smtpserver ```python # content of conftest.py import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(request): server = getattr(request.module, "smtpserver", "smtp.gmail.com") smtp_connection = smtplib.SMTP(server, 587, timeout=5) yield smtp_connection print("finalizing {} ({})".format(smtp_connection, server)) smtp_connection.close() ``` ```python # content of test_anothersmtp.py smtpserver = "mail.python.org" # will be read by smtp fixture def test_showhelo(smtp_connection): assert 0, smtp_connection.helo() ``` 示例2,結合request+mark,把fixt_data從test_fixt傳值給了fixt ```python import pytest @pytest.fixture def fixt(request): marker = request.node.get_closest_marker("fixt_data") if marker is None: # Handle missing marker in some way... data = None else: data = marker.args[0] # Do something with the data return data @pytest.mark.fixt_data(42) def test_fixt(fixt): assert fixt == 42 ``` # fixture setup / teardown 其他測試框架unittest/testng,都定義了setup和teardown函式/方法,用來測試前初始化和測試後清理。 pytest也有,不過是相容unittest等弄的,**不推薦!** ```python from loguru import logger def setup(): logger.info("setup") def teardown(): logger.info("teardown") def test(): pass ``` **建議使用fixture。** **setup**,fixture可以定義autouse來實現初始化。 ```python @pytest.fixture(autouse=True) ``` autouse的fixture不需要呼叫,會自己執行,和test放到相同scope,就能實現setup的效果。 autouse使用說明 - autouse遵循scope的規則,scope="session"整個會話只會執行1次,其他同理 - autouse定義在module中,module中的所有function都會用它(如果scope="module",只執行1次,如果scope="function",會執行多次) - autouse定義在conftest.py,conftest覆蓋的test都會用它 - autouse定義在plugin中,安裝plugin的test都會用它 - **在使用autouse時需要同時注意scope和定義位置** 示例,transact預設scope是function,會在每個test函式執行前自動執行 ```python # content of test_db_transact.py import pytest class DB: def __init__(self): self.intransaction = [] def begin(self, name): self.intransaction.append(name) def rollback(self): self.intransaction.pop() @pytest.fixture(scope="module") def db(): return DB() class TestClass: @pytest.fixture(autouse=True) def transact(self, request, db): db.begin(request.function.__name__) yield db.rollback() def test_method1(self, db): assert db.intransaction == ["test_method1"] def test_method2(self, db): assert db.intransaction == ["test_method2"] ``` 這個例子不用autouse,用conftest.py也能實現 ```python # content of conftest.py @pytest.fixture def transact(request, db): db.begin() yield db.rollback() ``` ```python @pytest.mark.usefixtures("transact") class TestClass: def test_method1(self): ... ``` **teardown**,可以在fixture中使用yield關鍵字來實現清理。 示例,scope為module,在module結束時,會執行yield後面的print()和smtp_connection.close() ```python # content of conftest.py import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(): smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) yield smtp_connection # provide the fixture value print("teardown smtp") smtp_connection.close() ``` 可以使用with關鍵字進一步簡化,with會自動清理上下文,執行smtp_connection.close() ```python # content of test_yield2.py import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(): with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection: yield smtp_connection # provide the fixture value ``` # fixture引數化 後續會專門講“pytest引數化”,這裡就先跳過,請各位見諒啦。 因為我覺得想用pytest做引數化,一定是先到引數化的文章裡面找,而不是到fixture。 把這部分放到引數化,更便於以後檢索。 # 簡要回顧 本文開頭通過原始碼介紹了fixture是什麼,並簡單總結定義和用法。然後對依賴注入進行了解釋,以更好理解fixture技術的原理。入門示例給出了官網的例子,以此展開講了範圍、順序、巢狀、傳值,以及初始化和清理的知識。 如果遇到問題,歡迎溝通討論。 更多實踐內容,請關注後續篇章《tep最佳實踐》。 *參考資料* https://en.wikipedia.org/wiki/Dependency_injection https://en.wikipedia.org/wiki/Inversion_of_control https://docs.pytest.org/en/stable/contents.html#toc 版權申明:本文為博主原創文章,轉載請保留原文連結及作者。 如果您喜歡我寫的文章,請關注公眾號支援一下,謝謝哈哈哈。

相關推薦

pytest 精通fixture

首先放一句“狠話”。 如果你不會fixture,那麼你最好別說自己會pytest。 (只是為了烘托主題哈,手上的磚頭可以放下了,手動滑稽) # fixture是什麼 看看原始碼 ```python def fixture( callable_or_scope=None, *args,

pytest 內建和自定義marker

可以通過命令列檢視所有marker,包括內建和自定義的 ```shell pytest --markers ``` # 內建marker 內建marker本文先講usefixtures 、filterwarnings 、skip 、skipif 、xfail這5個。引數化的marker我會寫在

pytest 引數化進階

用過unittest的朋友,肯定知道可以藉助DDT實現引數化。用過JMeter的朋友,肯定知道JMeter自帶了4種引數化方式(見參考資料)。pytest同樣支援引數化,而且很簡單很實用。 # 語法 在《pytest封神之路第三步 精通fixture》和《pytest封神之路第四步 內建和自定義marke

pytest第一 tep介紹

『 tep is a testing tool to help you write pytest more easily. Try Easy Pytest! 』 # tep前身 tep的前身是介面自動化測試框架pyface,一款面向物件設計的測試框架,我寫過一篇[部落格](https://www.cn

《帶你裝B,帶你飛》pytest2- 執行用例規則和pycharm執行的種姿態

1. 簡介   今天北京下的雪好大好美啊!!!哎呀,忘記拍照片了,自己想象一下吧。言歸真傳,今天還是開始pytest的學習和修煉,上一篇寫完後群裡反響各式各樣的,幾家歡樂幾家愁,有的高興說自己剛好要用到了,正好一起學習,有的不開心說自己介面還沒有學完了,沒關係的學習本來就不是一件一蹴而就的事情,需要日積月累

python

區別 英語 utf-8 系統 存在 創建 tel run 變量名 python文件目錄操作 python中對文件、文件夾(文件操作函數)的操作需要涉及到os模塊和shutil模塊。 得到當前工作目錄,即當前Python腳本工作的目錄路

python學習——彈 (作業篇第一題)

image 操作 啟動程序 代碼 color 鎖定文件 文件 文件內容 數據 作業一:編寫登錄接口1.輸入用戶名密碼2.認證成功後顯示歡迎信息3.輸錯三次後鎖定。 所需知識點 文件基本讀寫操作,循環,列表,字典 上面的作業題是在學習完數據類型和簡單的文件操作之後布置的,

重構篇——重新組織數據

chan direction hang rate state elf with bsp 類型 本篇目錄: 1 Self Encapsulate Field(自封裝字段) 2 Replace Data Value with Object(以對象取代數據值) 3 Change

python學習-天-一個簡單的腳本

tro 說明 .py else zipfile rect dylib 環境 cef 現在有一個需求:把某個目錄下的文件備份到指定到另外一個目錄下,而且壓縮後文件為zip文件 # -*- coding:utf-8 -*- #! /usr/bin/python # Filena

Python人工智能 - 篇 : PyAudio 實現錄音 自動化交互實現問答

獲得 本地文件 一次 cor ets win 不清晰 考題 dbo Python 很強大其原因就是因為它龐大的三方庫 , 資源是非常的豐富 , 當然也不會缺少關於音頻的庫 關於音頻, PyAudio 這個庫, 可以實現開啟麥克風錄音, 可以播放音頻文件等等,此刻我們不去了解

Django--

creat () roo object migrate filter sta migration upd 1.ORM1.1.創建類和字段 class UserInfo(models.Model): name=models.CharField(max_length=6

ocrosoft Contest1316 - 信奧編程~~~~~關 問題 I: 尋找大富翁

EDA 第三關 using cpp print int ocr mes name http://acm.ocrosoft.com/problem.php?cid=1316&pid=8 題目描述 浙江杭州某鎮共有n個人,請找出該鎮上的前m個大富翁. 輸入

java學習---------

今天主要就是陣列的基本概念 陣列:例子            int  arr[] = new int[3];      &nb

Python(十一篇) 網路程式設計:簡單的tcp套接字通訊、粘包現象

  一、簡單的tcp套接字通訊 套接字通訊的一般流程 服務端 server = socket() #建立伺服器套接字 server.bind() #把地址繫結到套接字,網路地址加埠 server.listen() #監聽連結 inf_loop:

Python(十三篇) 網路程式設計:socketserver深度解析

一、socketserver 模組介紹 socketserver是標準庫中的一個高階模組,用於網路客戶端與伺服器的實現。(version = "0.4") 在python2中寫作SocketServer,在python3中寫作socketserver。 socoketserver兩個主要的類,一個是S

Python(十四篇) 網路程式設計:驗證客戶端合法性

一、驗證客戶端合法性 如果你想在分散式系統中實現一個簡單的客戶端連結認證功能,又不像SSL那麼複雜,那麼利用hmac+加鹽的方式來實現。   客戶端驗證的總的思路是將服務端隨機產生的指定位數的位元組傳送到客戶端,兩邊同時用hmac進行加密,然後對生成的密文進行比較,相同就是合法的客戶端,不相同就是不合法

阿里P7/P8學習路線圖——技術

一、基礎篇 JVM JVM記憶體結構 堆、棧、方法區、直接記憶體、堆和棧區別 Java記憶體模型 記憶體可見性、重排序、順序一致性、volatile、鎖、final 垃圾回收 記憶體分配策略、垃圾收集器(G1)、GC演算法、GC引數、物件存活的判定 JVM引數及

黎想深度訪談騰訊頂級產品經理的進階——篇《需求》

16個月精心打磨,9位頂級產品專家研討提煉,凝聚騰訊產品經驗的八集八分鐘產品課分別從使用者、定位、需求、時機、匠心、危機、合作、商業角度出發,還原產品背後的故事,分享給你騰訊產品的心法。藝形藝意工作室創始人黎想將深度訪談騰訊頂級產品經理的進階之路,邀您一起探索一

Python(十七篇)併發程式設計:程序、multiprocess模組、建立程序方式、join()、守護程序

一、在python程式中的程序操作  之前已經瞭解了很多程序相關的理論知識,瞭解程序是什麼應該不再困難了,執行中的程式就是一個程序。所有的程序都是通過它的父程序來建立的。因此,執行起來的python程式也是一個程序,那麼也可以在程式中再建立程序。多個程序可以實現併發效果,也就是說,當程式中存在多個程序的時候

Python(十八篇) 併發程式設計:程序同步鎖/互斥鎖、訊號量、事件、佇列、生產者消費者模型

一、程序鎖(同步鎖/互斥鎖) 程序之間資料不共享,但是共享同一套檔案系統,所以訪問同一個檔案,或同一個列印終端,是沒有問題的, 而共享帶來的是競爭,競爭帶來的結果就是錯亂,如何控制,就是加鎖處理。 例子 #併發執行,效率高,但競爭同一列印終端,帶來了列印錯亂 from multiproc