编写测试函数
一个规范的测试方法一定带有断言,在使用pytest时,可以直接使用Python自带的assert关键字
PytestUnittestassert somethingassertTrue(something)assert a==bassertEqual(a,b)assert a<=bassertLessEqual(a,b)……Pytest允许在assert关键字添加任意表达式,表达式的值通过bool转换后等于False则表示断言失败,测试用例执行失败;如果等于True则表示断言成功,测试用例执行成功。
重写assert
pytest可以重写assert关键字,它可以截断对原生assert的调用,替换为pytest定义的assert,从而展示更详细的信息和细节。
from collections import namedtuple
Task = namedtuple('Task',['summary','owner','done','id'])# __new__.__defaults__创建默认的Task对象
Task.__new__.__defaults__ =(None,None,False,None)import pytest
deftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)assert t1 == t2
deftest_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)assert t1_dict == t2_dict
执行结果如下:
E:\Programs\Python\Python_Pytest\TestScripts>pytest test_three.py
=========================================== test session starts ==============================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_three.py FF [100%]================================================ FAILURES ====================================================
___________________________________________ test_task_equality _______________________________________________
deftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Use -v to get the full diff
test_three.py:14: AssertionError
---------------------------------------- Captured stdout call ------------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)
___________________________________________ test_dict_equal __________________________________________________
deftest_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)>assert t1_dict == t2_dict
E AssertionError:assert OrderedDict([...('id',None)])== OrderedDict([(...('id',None)])
E Omitting 3 identical items, use -vv to show
E Differing items:
E {'owner':'okken'}!={'owner':'okkem'}
E Use -v to get the full diff
test_three.py:22: AssertionError
------------------------------------------- Captured stdout call ---------------------------------------------
OrderedDict([('summary','make sandwich'),('owner','okken'),('done',False),('id',None)])
OrderedDict([('summary','make sandwich'),('owner','okkem'),('done',False),('id',None)])==========================================2 failed in0.20 seconds===========================================
加上参数-v,执行结果如下:
E:\Programs\Python\Python_Pytest\TestScripts>pytest test_three.py -v
=========================================== test session starts ==============================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_three.py::test_task_equality FAILED [50%]
test_three.py::test_dict_equal FAILED [100%]================================================ FAILURES ===================================================
___________________________________________ test_task_equality ______________________________________________
deftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Full diff:
E - Task(summary='sit there', owner='brain', done=False,id=None)
E ? ^^^^^^^^^^
E + Task(summary='do something', owner='okken', done=False,id=None)
E ? +++^^^^^^^^^^
test_three.py:14: AssertionError
----------------------------------------- Captured stdout call -----------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)
___________________________________________ test_dict_equal __________________________________________________
deftest_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)>assert t1_dict == t2_dict
E AssertionError:assert OrderedDict([...('id',None)])== OrderedDict([(...('id',None)])
E Omitting 3 identical items, use -vv to show
E Differing items:
E {'owner':'okken'}!={'owner':'okkem'}
E Full diff:
E OrderedDict([('summary','make sandwich'),
E -('owner','okken'),
E ? ^...
E
E ...Full output truncated (5 lines hidden), use '-vv' to show
test_three.py:22: AssertionError
-------------------------------------------- Captured stdout call --------------------------------------------
OrderedDict([('summary','make sandwich'),('owner','okken'),('done',False),('id',None)])
OrderedDict([('summary','make sandwich'),('owner','okkem'),('done',False),('id',None)])==========================================2 failed in0.13 seconds ==========================================
预期异常
"""
在Task项目的API中,有几个地方可能抛出异常
def add(task): # type:(Task) ->int
def get(task_id): # type:(int) ->Task
def list_tasks(owner=None): # type:(str|None) ->list of task
def count(): # type:(None) ->int
def update(task_id, task): # type:(int, Task) ->None
def delete(task_id): # type:(int) ->None
def delete_all(): # type:() ->None
def unique_id(): # type:() ->int
def start_tasks_db(db_path, db_type): # type:(str, str) ->None
def stop_tasks_db(): # type:() ->None
"""import pytest
import tasks
deftest_add_raises():with pytest.raises(TypeError):
tasks.add(task='no a Task object')"""
测试用例中有with pytest.raise(TypeError)生命,意味着无论with结构中的内容是什么
都至少会发生TypeError异常,如果测试通过,说明确实发生了我们预期TypeError,如果抛出的是其他类型的异常
则与我们预期的异常不一致,测试用例执行失败
"""deftest_start_tasks_db_raises():with pytest.raises(ValueError)as excinfo:
tasks.start_tasks_db('some/great/path','mysql')
exception_msg = excinfo.value.args[0]assert exception_msg =="db_type must be a 'tiny' or 'mongo' "
测试函数的标记
from collections import namedtuple
import pytest
Task = namedtuple('Task',['summary','owner','done','id'])# __new__.__defaults__创建默认的Task对象
Task.__new__.__defaults__ =(None,None,False,None)@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)assert t1 == t2
@[email protected]_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)assert t1_dict == t2_dict
执行结果如下:
E:\Programs\Python\Python_Pytest\TestScripts>pytest -v -m 'smoke' test_five.py
======================================== test session starts =================================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_five.py::test_task_equality FAILED [50%]
test_five.py::test_dict_equal FAILED [100%]================================================ FAILURES ====================================================
__________________________________________ test_task_equality ________________________________________________
@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Full diff:
E - Task(summary='sit there', owner='brain', done=False,id=None)
E ? ^^^^^^^^^^
E + Task(summary='do something', owner='okken', done=False,id=None)
E ? +++^^^^^^^^^^
test_five.py:14: AssertionError
--------------------------------------- Captured stdout call -------------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)
___________________________________________ test_dict_equal __________________________________________________
@[email protected]_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)>assert t1_dict == t2_dict
E AssertionError:assert OrderedDict([...('id',None)])== OrderedDict([(...('id',None)])
E Omitting 3 identical items, use -vv to show
E Differing items:
E {'owner':'okken'}!={'owner':'okkem'}
E Full diff:
E OrderedDict([('summary','make sandwich'),
E -('owner','okken'),
E ? ^...
E
E ...Full output truncated (5 lines hidden), use '-vv' to show
test_five.py:24: AssertionError
--------------------------------------- Captured stdout call -------------------------------------------------
OrderedDict([('summary','make sandwich'),('owner','okken'),('done',False),('id',None)])
OrderedDict([('summary','make sandwich'),('owner','okkem'),('done',False),('id',None)])============================================ warnings summary ================================================
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.smoke -is this a typo? You can register custom marks to avoi
d this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.dict-is this a typo? You can register custom marks to avoid
this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,-- Docs: https://docs.pytest.org/en/latest/warnings.html
===================================2 failed,2 warnings in0.22 seconds =====================================
在命令行执行使用了 -m marker_name参数,pytest在执行时会自动寻找被标记为marker_name的测试方法去执行,同时-m还支持and、or、not关键字,如下方式:
E:\Programs\Python\Python_Pytest\TestScripts>pytest -v -m "smoke and not dict" test_five.py
========================================= test session starts ================================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items /1 deselected /1 selected
test_five.py::test_task_equality FAILED [100%]============================================= FAILURES =======================================================
_______________________________________ test_task_equality ___________________________________________________
@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Full diff:
E - Task(summary='sit there', owner='brain', done=False,id=None)
E ? ^^^^^^^^^^
E + Task(summary='do something', owner='okken', done=False,id=None)
E ? +++^^^^^^^^^^
test_five.py:14: AssertionError
--------------------------------------- Captured stdout call ------------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)========================================= warnings summary ===================================================
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.smoke -is this a typo? You can register custom marks to avoi
d this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.dict-is this a typo? You can register custom marks to avoid
this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,-- Docs: https://docs.pytest.org/en/latest/warnings.html
=============================1 failed,1 deselected,2 warnings in0.14 seconds =============================
跳过测试
from collections import namedtuple
import pytest
Task = namedtuple('Task',['summary','owner','done','id'])# __new__.__defaults__创建默认的Task对象
Task.__new__.__defaults__ =(None,None,False,None)@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)assert t1 == t2
@[email protected]@pytest.mark.skip(reason="跳过原因你不知道吗?")deftest_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)assert t1_dict == t2_dict
E:\Programs\Python\Python_Pytest\TestScripts>pytest -v test_five.py
======================================= test session starts ==================================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_five.py::test_task_equality FAILED [50%]
test_five.py::test_dict_equal SKIPPED [100%]============================================== FAILURES ======================================================
_____________________________________________ test_task_equality _____________________________________________
@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Full diff:
E - Task(summary='sit there', owner='brain', done=False,id=None)
E ? ^^^^^^^^^^
E + Task(summary='do something', owner='okken', done=False,id=None)
E ? +++^^^^^^^^^^
test_five.py:14: AssertionError
--------------------------------------- Captured stdout call -------------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)========================================= warnings summary ===================================================
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.smoke -is this a typo? You can register custom marks to avoi
d this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.dict-is this a typo? You can register custom marks to avoid
this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,-- Docs: https://docs.pytest.org/en/latest/warnings.html
=============================1 failed,1 skipped,2 warnings in0.21 seconds ================================
从测试结果中我们看到,测试用例test_five.py::test_dict_equal SKIPPED 被跳过了,然而跳过原因并没有如期的展示出来,可以使用参数-rs来展示跳过原因,如下方式:
E:\Programs\Python\Python_Pytest\TestScripts>pytest -rs test_five.py
==================================== test session starts =====================================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_five.py Fs [100%]========================================== FAILURES ==========================================================
________________________________________ test_task_equality __________________________________________________
@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)>assert t1 == t2
E AssertionError:assert Task(summary=...alse,id=None)== Task(summary='...alse,id=None)
E At index 0 diff:'sit there'!='do something'
E Use -v to get the full diff
test_five.py:14: AssertionError
-------------------------------------------- Captured stdout call --------------------------------------------
Task(summary='sit there', owner='brain', done=False,id=None)
Task(summary='do something', owner='okken', done=False,id=None)============================================== warnings summary ==============================================
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.smoke -is this a typo? You can register custom marks to avoi
d this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
c:\python37\lib\site-packages\_pytest\mark\structures.py:324
c:\python37\lib\site-packages\_pytest\mark\structures.py:324: PytestUnknownMarkWarning: Unknown pytest.mark.dict-is this a typo? You can register custom marks to avoid
this warning -for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,-- Docs: https://docs.pytest.org/en/latest/warnings.html
==================================== short test summary info =================================================
SKIPPED [1] test_five.py:17: 跳过原因你不知道吗?
==============================1 failed,1 skipped,2 warnings in0.14 seconds ===============================
还可以给跳过添加理由,例如当selenium版本是1.0.4的时候跳过
from collections import namedtuple
import pytest
import selenium
Task = namedtuple('Task',['summary','owner','done','id'])# __new__.__defaults__创建默认的Task对象
Task.__new__.__defaults__ =(None,None,False,None)@pytest.mark.smokedeftest_task_equality():
t1 = Task('sit there','brain')
t2 = Task('do something','okken')print(t1)print(t2)assert t1 == t2
@[email protected]@pytest.mark.skipif(selenium.__version__ =='1.0.4', reason="跳过原因你不知道吗?")deftest_dict_equal():
t1_dict = Task('make sandwich','okken')._asdict()
t2_dict = Task('make sandwich','okkem')._asdict()print(t1_dict)print(t2_dict)assert t1_dict == t2_dict
运行测试子集
运行单个目录,只需要将目录作为pytest的参数即可
pytest -v test/func --tb=no
运行单个测试函数,只需要在文件名后添加::符号和函数名
pytest -v test/func/test_five.py::test_dict_equal
运行单个测试类,与运行单个测试函数类似,在文件名后添加::符号和类名
pytest -v test/func/test_five.py::Test_Update
运行单个测试类中的测试方法,在文件名后添加::符号和类名然后再跟::符号和函数名
pytest -v test/func/test_five.py::Test_Update::test_dict_equal
定制fixture
在pytest中的fixture是在测试函数运行前后,由pytest执行的外壳函数,fixture中的代码可以定制,满足多变的测试需求:包括定义传入测试中的数据集、配置测试前系统的初始化状态、为批量测试提供数据源。
import pytest
@pytest.fixture()defreturn_data():return1000deftest_someting(return_data):assert return_data ==1000
执行结果如下:
(venv) E:\Programs\Python\Python_Pytest\TestScripts>pytest -v test_fixture.py
============================================ test session starts =============================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\TestScripts
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 1 item
test_fixture.py::test_someting PASSED [100%]=========================================1 passed in0.10 seconds ===========================================
通过conftest.py共享fixture
fixture可以放在单独的测试文件里,也可以通过在某个公共目录下新建一个conftest.py文件,将fixture放在其中实现多个测试文件共享fixture。
conftest.py是Python模块,但他不能被测试文件导入,pytest视其为本地插件库。
使用fixture执行配置及销毁逻辑
借助fixture的yield实现setup和teardown
# encoding = utf-8import pytest
from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()
url ="https://www.baidu.com"@pytest.fixture(scope='module')defautomation_testing():# 执行测试前
driver.get(url)yield# 测试结束后
driver.quit()deftest_fixture(automation_testing):
driver.find_element_by_id("kw").send_keys("davieyang")
driver.find_element_by_id("su").click()
sleep(10)
执行结果如下:
DY@MacBook-Pro TestScripts$pytest -v test_seven.py
========================================= test session starts ================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0--/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
cachedir:.pytest_cache
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 1 item
test_seven.py::test_fixture PASSED [100%]==========================================1 passed in20.45s ================================================
使用–setup-show回溯fixture的执行过程
同样的代码,在执行命令中加上参数
--setup-show
,便可以看到fixture中实际的执行过程,执行结果如下:
DY@MacBook-Pro TestScripts$pytest --setup-show test_seven.py
========================================= test session starts ================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 1 item
test_seven.py
SETUP F automation_testing
test_seven.py::test_fixture (fixtures used: automation_testing).
TEARDOWN F automation_testing
=========================================1 passed in14.69s =================================================
使用fixture传递测试数据
fixture非常适合存放测试数据,并且它可以返回任何数据
# encoding = utf-8import pytest
@pytest.fixture()defreturn_tuple():return('davieyang','36',{'mail':'[email protected]'})deftest_return_tuple(return_tuple):assert return_tuple[2]['mail']=='[email protected]'
执行结果如下:
DY@MacBook-Pro TestScripts$pytest --setup-show -v test_seven.py
======================================== test session starts ================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0--/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
cachedir:.pytest_cache
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 1 item
test_seven.py::test_return_tuple
SETUP F return_tuple
test_seven.py::test_return_tuple (fixtures used: return_tuple)FAILED
TEARDOWN F return_tuple
============================================ FAILURES ========================================================
____________________________________________ test_return_tuple _______________________________________________
return_tuple =('davieyang','36',{'mail':'[email protected]'})deftest_return_tuple(return_tuple):>assert return_tuple[2]['mail']=='[email protected]'
E AssertionError:assert'[email protected]'=='[email protected]'
E - [email protected]
E + [email protected]
E ? +
test_seven.py:11: AssertionError
=========================================1 failed in0.08s ==================================================
假设在fixture函数中出现了异常,那么执行结果中报的会是Error而不是Fail
DY@MacBook-Pro TestScripts$pytest --setup-show -v test_seven.py
======================================== test session starts =================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0--/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
cachedir:.pytest_cache
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 0 items /1 errors
============================================= ERRORS =========================================================
_______________________________________ ERROR collecting test_seven.py _______________________________________
如果是测试函数异常,那么会报Fail而不是Error
指定fixture作用范围
Fixture中包含一个叫scope的可选参数,用于控制fixture执行配置和销毁逻辑的频率,该参数有4个值分别是:function、class、module和session,其中function为默认值。
ScopeDescriptionfunction函数级别的Fixture每个测试函数只需要运行一次,配置代码在测试用例运行之前运行,销毁代码在测试用例运行之后运行class类级别的Fixture每个测试类只需要运行一次,无论测试类中有多少类方法都可以共享这个fixturesession会话级别的Fixture每次会话只需要运行一次,一次pytest会话中的所有测试函数、方法都可以共享这个fixturemodule模块级别的Fixture每个模块只需要运行一次,无论模块里有多少测试函数、类方法或者其他fixture都可以共享这个fixture
import pytest
@pytest.fixture(scope='function')deffunc_scope():"""The function scope fixture"""@pytest.fixture(scope='module')defmod_scope():"""The module scope fixture"""@pytest.fixture(scope='class')defclass_scope():"""The class scope fixture"""@pytest.fixture(scope='session')defsess_scope():"""The session scope fixture"""deftest_one(sess_scope, mod_scope, func_scope):"""
Test using session, module, and function scope fixtures
:param sess_scope:
:param mod_scope:
:param func_scope:
:return:
"""deftest_two(sess_scope, mod_scope, func_scope):"""
Again!
:param sess_scope:
:param mod_scope:
:param func_scope:
:return:
"""@pytest.mark.usefixtures('class_scope')classTestSomething:deftest_three(self):"""
Test using class scope fixture
:return:
"""deftest_four(self):"""
Again
:return:
"""
执行结果如下:
DY@MacBook-Pro TestScripts$pytest --setup-show test_eight.py
========================================== test session starts ===============================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 4 items
test_eight.py
SETUP S sess_scope
SETUP M mod_scope
SETUP F func_scope
test_eight.py::test_one (fixtures used: func_scope, mod_scope, sess_scope).
TEARDOWN F func_scope
SETUP F func_scope
test_eight.py::test_two (fixtures used: func_scope, mod_scope, sess_scope).
TEARDOWN F func_scope
SETUP C class_scope
test_eight.py::TestSomething::test_three (fixtures used: class_scope).
test_eight.py::TestSomething::test_four (fixtures used: class_scope).
TEARDOWN C class_scope
TEARDOWN M mod_scope
TEARDOWN S sess_scope
===========================================4 passed in0.07s ================================================
从执行结果中,可以看到不但出现了代表函数级别和会话级别的F和S,还出现了代表类级别和模块级别的C和M
scope参数是在定义fixture时定义的,而不是在调用fixture时定义,使用fixture的测试函数是无法改变fixture作用范围的
fixture只能使用同级别的fixture或者高级别的fixture,比如函数级别的fixture可以使用同级别的fixture,也可以使用类级别、模块级别和会话级别的fixture,反过来则不可以
使用usefixtures指定fixture
在之前的代码示例中都是在测试函数的参数列表中指定fixture,除了这种方法可以让测试函数使用fixture外,还可以使用@pytest.mark.usefixtures(‘fixture1’, ‘fixture2’)标记测试函数或测试类,虽然这种方式这对测试函数来说意义不大,因为分别放在测试函数的参数中跟为每个函数做这样的标记比起来或许更简单,但是对于测试类来说,使用这种标记的方式就简便了很多。
@pytest.mark.usefixtures('class_scope')classTestSomething:deftest_three(self):"""
Test using class scope fixture
:return:
"""deftest_four(self):"""
Again
:return:
"""
使用usefixtures和在测试方法中指定fixture参数大体上差不多,但只有后者才能够使用fixture的返回值
为常用fixture添加autouse选项
通过指定autouse=True使作用域内的测试函数都运行该fixture,在需要多次运行但不依赖任何的系统状态或者外部数据的代码配合起来更加便利。
import pytest
import time
@pytest.fixture(autouse=True, scope='session')deffooter_session_scope():"""Report the time at the end of a session"""yield
now = time.time()print('- -')print('finish:{}'.format(time.strftime('%d %b %X', time.localtime(now))))print('---------------')@pytest.fixture(autouse=True)deffooter_function_scope():"""Report test durations after each function"""
start = time.time()yield
stop = time.time()
delta = stop - start
print('\ntest duration:{:0.3} seconds'.format(delta))deftest_one():"""Simulate long-ish running test."""
time.sleep(1)deftest_two():"""Simulate slightly longer test"""
time.sleep(1.23)
执行结果如下:
DY@MacBook-Pro TestScripts$pytest -v -s test_nine.py
======================================== test session starts =================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0--/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
cachedir:.pytest_cache
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 2 items
test_nine.py::test_one PASSED
test duration:1.01 seconds
test_nine.py::test_two PASSED
test duration:1.24 seconds
--
finish:15 Sep 17:12:25---------------==============================================2 passed in2.26s =============================================
为fixture重命名
import pytest
@pytest.fixture(name='davieyang')defrename_fixture_as_davieyang():""""""return36deftest_rename_fixture(davieyang):assert davieyang ==36
执行结果如下:
DY@MacBook-Pro TestScripts$pytest --setup-show test_ten.py -v
========================================= test session starts ================================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0--/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6
cachedir:.pytest_cache
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 1 item
test_ten.py::test_rename_fixture
SETUP F davieyang
test_ten.py::test_rename_fixture (fixtures used: davieyang)PASSED
TEARDOWN F davieyang
==============================================1 passed in0.01s =============================================
如果想找到‘davieyang’在哪里定义的,则可以使用pytest的参数 --fixtures
DY@MacBook-Pro TestScripts$pytest --fixtures test_ten.py
========================================== test session starts ===============================================
platform darwin -- Python 3.6.5, pytest-5.1.2, py-1.8.0, pluggy-0.13.0
rootdir:/Volumes/Extended/PythonPrograms/Pytest/TestScripts
collected 1 item
------------------------------- fixtures defined from TestScripts.test_ten -----------------------------------
davieyang
Return where we renamed fixture
========================================= no tests ran in0.04s ==============================================
Fixture的参数化
对测试函数进行参数化,可以多次运行的只是该测试函数;对fixture进行参数化,每个使用该fixture的测试函数都可以执行多次
task_tuple =(Task('sleep',None,False),
Task('wake','brain',False),
Task('breathe','BRAIN',True),
Task('exercise','BrIaN',False))
task_ids =['Task({},{},{})'.format(t.summary, t.owner, t.done)for t in task_tuple]defequivalent(t1, t2):"""Check two tasks for equivalence"""return((t1.summary == t2.summart)and(t1.owner == t2.owner)and(t1.done == t2.done))@pytest.fixture(params=task_tuple)defa_task(request):return request.param
deftest_dosomething(tasks_db, a_task):
task_id = tasks.add(a_task)
t_from_db = tasks.get(task_id)assert equivalent(t_from_db, a_task)
task_tuple =(Task('sleep',None,False),
Task('wake','brain',False),
Task('breathe','BRAIN',True),
Task('exercise','BrIaN',False))
task_ids =['Task({},{},{})'.format(t.summary, t.owner, t.done)for t in task_tuple]defequivalent(t1, t2):"""Check two tasks for equivalence"""return((t1.summary == t2.summart)and(t1.owner == t2.owner)and(t1.done == t2.done))@pytest.fixture(params=task_tuple, ids=task_ids)defb_task(request):return request.param
deftest_dosomething(tasks_db, b_task)):
task_id = tasks.add(b_task)
t_from_db = tasks.get(task_id)assert equivalent(t_from_db, b_task)
task_tuple =(Task('sleep',None,False),
Task('wake','brain',False),
Task('breathe','BRAIN',True),
Task('exercise','BrIaN',False))defid_function(fixture_value):
t = fixture_value
return'Task({},{},{})'.format(t.summary, t.owner, t.done)defequivalent(t1, t2):"""Check two tasks for equivalence"""return((t1.summary == t2.summart)and(t1.owner == t2.owner)and(t1.done == t2.done))@pytest.fixture(params=task_tuple, ids=id_function)defc_task(request):return request.param
deftest_dosomething(tasks_db, c_task):
task_id = tasks.add(c_task)
t_from_db = tasks.get(task_id)assert equivalent(t_from_db, c_task)
编写插件及分享
基于Pytest的代码结构,可以借助hook函数来实现定制和扩展插件,将Fixture和Hook函数添加到conftest.py文件里,就已经创建了一个本地conftest插件,然后将conftest.py文件转换为可安装的插件然后再与其他人分享。
获取第三方插件
已经有很多人开发了自己的插件,通过如下地址可以找到很多实用或者有意思的插件供我们使用。
https://docs.pytest.org/en/latest/plugins.html
https://pypi.python.org
https://github.com/pytest-dev
安装插件
常规安装
pip install pytest-cov
pip install pytest-cov==2.5.1
pip install pytest-cov-2.5.1.tar.gz
pip install pytest_cov-2.5.1-py2.py3-none-any.whl
本地安装插件
mkdir some_plugins
cp pytest_cov-2.5.1-py2.py3-none-any.whl some_plugins/
pip install --no-index --find-links=./some_plugins/ pytest-cov
pip install --no-index --find-links=./some_plugins/ pytest-cov==2.5.1
–no-index 告诉pip不要链接PyPI
–find-links=./some_plugins/ 告诉pip安装时候查找安装文件的目录
从Git安装插件
pip install git+https://github.com/pytest-dev/pytest-cov
pip install git+https://github.com/pytest-dev/[email protected]
pip install git+https://github.com/pytest-dev/pytest-cov@master
编写自己的插件
开发pytest的目的之一就是用插件改变pytest的运行方式,而hook函数是编写插件的利器之一,可以通过hookspect在本例中,会将错误信息修改为OPPORTINITY for improvement,而将F的状态改为O,并在标题中添加Thanks for running the tests,然后使用–nice来打开新增功能。
还是以Task项目为例,列举两个例测试,代码如下:
"""Test for expected exceptions from using the API wrong."""import pytest
import tasks
from tasks import Task
@pytest.mark.usefixtures('tasks_db')classTestAdd:"""Tests related to tasks.add()."""deftest_missing_summary(self):"""Should raise an exception if summary missing."""with pytest.raises(ValueError):
tasks.add(Task(owner='bob'))deftest_done_not_bool(self):"""Should raise an exception if done is not a bool."""with pytest.raises(ValueError):
tasks.add(Task(summary='summary', done='True'))
然后我们在conftest.py代码新增:
@pytest.fixture(scope='session')deftasks_db_session(tmpdir_factory, request):"""Connect to db before tests, disconnect after."""
temp_dir = tmpdir_factory.mktemp('temp')
tasks.start_tasks_db(str(temp_dir),'tiny')yield# this is where the testing happens
tasks.stop_tasks_db()@pytest.fixture()deftasks_db(tasks_db_session):"""An empty tasks db."""
tasks.delete_all()
执行结果:
D:\PythonPrograms\Python_Pytest\TestScripts>pytest test_api_exceptions.py
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.0.2, py-1.8.0, pluggy-0.12.0
rootdir: D:\PythonPrograms\Python_Pytest\TestScripts, inifile:
plugins: allure-adaptor-1.7.10, cov-2.7.1
collected 2 items
test_api_exceptions.py .F [100%]================================== FAILURES ===================================
_________________________ TestAdd.test_done_not_bool __________________________
self =<TestScripts.test_api_exceptions.TestAdd object at 0x000000B58386DF28>deftest_done_not_bool(self):"""Should raise an exception if done is not a bool."""with pytest.raises(ValueError):> tasks.add(Task(summary='summary', done='True'))
E Failed: DID NOT RAISE <class'ValueError'>
test_api_exceptions.py:19: Failed
=================1 failed,1 passed in0.13 seconds ======================
接下来借助hook函数,将thanks …消息添加进去并且将F更改为O,将FAILED改为OPPORTUNITY for improvement,在conftest.py新增:
defpytest_report_header():"""Thank tester for running tests."""return "Thanks for running the tests.defpytest_report_teststatus(report):"""Turn failures into opportunities."""if report.when =='call'and report.failed:return(report.outcome,'O','OPPORTUNITY for improvement')
在此执行:
D:\PythonPrograms\Python_Pytest\TestScripts>pytest -v --tb=no test_api_exceptions.py
===================== test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.0.2, py-1.8.0, pluggy-0.12.0-- c:\python37\python.exe
cachedir:.pytest_cache
Thanks for running the tests.
rootdir: D:\PythonPrograms\Python_Pytest\TestScripts, inifile:
plugins: allure-adaptor-1.7.10, cov-2.7.1
collected 2 items
test_api_exceptions.py::TestAdd::test_missing_summary PASSED [50%]
test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement [100%]================1 failed,1 passed in0.13 seconds ======================
使用pytest_addoption函数添加命令行选项,修改conftest.py文件:
defpytest_addoption(parser):"""Turn nice features on with --nice option."""
group = parser.getgroup('nice')
group.addoption("--nice", action="store_true",help="nice: turn failures into opportunities")defpytest_report_header():"""Thank tester for running tests."""if pytest.config.getoption('nice'):return"Thanks for running the tests."defpytest_report_teststatus(report):"""Turn failures into opportunities."""if report.when =='call':if report.failed and pytest.config.getoption('nice'):return(report.outcome,'O','OPPORTUNITY for improvement')
同时在conftest.py同路径下新增pytest.ini文件,并在文件中写入:
[pytest]
nice=True
然后使用刚刚创建的–nice再次执行代码:
D:\PythonPrograms\Python_Pytest\TestScripts>pytest --nice --tb=no test_api_exceptions.py
===================== test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.0.2, py-1.8.0, pluggy-0.12.0
Thanks for running the tests.
rootdir: D:\PythonPrograms\Python_Pytest\TestScripts, inifile: pytest.ini
plugins: allure-adaptor-1.7.10, cov-2.7.1
collected 2 items
test_api_exceptions.py .O [100%]===============1 failed,1 passed in0.14 seconds ======================
加上-v再次执行一次:
(venv) E:\Programs\Python\Python_Pytest\TestScripts>pytest -v --nice --tb=no test_api_exceptions.py
============= test session starts ===============================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
Thanks for running the tests.
rootdir: E:\Programs\Python\Python_Pytest\TestScripts, inifile: pytest.ini
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 2 items
test_api_exceptions.py::TestAdd::test_missing_summary PASSED [50%]
test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement [100%]
在本例中我们使用了三个hook函数,实际上还有很多可用的hook,地址为writing_plugins
创建可安装的插件
将刚刚编写的插件变成可安装的插件并不难,首先在项目中创建这样一个目录结构
pytest-nice
|-------LICENCE
|-------README.md
|-------pytest_nice.py
|-------setup.py
|-------test
|-------conftest.py
|-------test_nice.py
然后将conftest.py中的hook函数转移到pytest_nice.py文件中,并将其从conftest.py中删除,然后pytest_nice.py文件中代码如下:
"""Code for pytest-nice plugin."""import pytest
defpytest_addoption(parser):"""Turn nice features on with --nice option."""
group = parser.getgroup('nice')
group.addoption("--nice", action="store_true",help="nice: turn FAILED into OPPORTUNITY for improvement")defpytest_report_header():"""Thank tester for running tests."""if pytest.config.getoption('nice'):return"Thanks for running the tests."defpytest_report_teststatus(report):"""Turn failures into opportunities."""if report.when =='call':if report.failed and pytest.config.getoption('nice'):return(report.outcome,'O','OPPORTUNITY for improvement')
在setup.py文件里调用setup()函数
"""Setup for pytest-nice plugin."""from setuptools import setup
setup(
name='pytest-nice',
version='0.1.0',
description='A pytest plugin to turn FAILURE into OPPORTUNITY',
url='https://wherever/you/have/info/on/this/package',
author='davieyang',
author_email='[email protected]',
license='proprietary',
py_modules=['pytest_nice'],
install_requires=['pytest'],
entry_points={'pytest11':['nice = pytest_nice',],},)
实际上setup()函数还有很多用于提供其他信息的参数,有兴趣可以自行研究这个函数。
setup()函数里的所有参数都是标准的,可用于所有python的安装程序,pytest插件的不同之处在于entry_point参数。
entry_points={'pytest11':['nice = pytest_nice',],},
entry_points是setuptools的标准功能,pytest11是一个特殊的标识符,通过这个设置,可以告诉pytest插件名称为nice,模块名称是pytest_nice,如果使用了包,则设置可以改成:
entry_points={'pytest11':['name_of_plugin=myprojectpluginmodule',],},
README.rst,setuptools要求必须提供README.rst文件,如果没有提供则会有警告
README.rst文件内容如下:
pytest-nice:A pytest plugin
===========================================================
Makes pytest output just a bit nicer during failures.
Features
---------Includs user name of person running tests in pytest output.-Add "--nice" option that:-turns "F" to "O"-with"-v", turns "FAILURE" to "OPPORTUNITY for improvement"
Installation
------------
Given that our pytst plugins are being saved in.rat.gz form in the
shared directory PATH, then install like this:::
$pip install PATH/pytest-nice-0.1.0.tar.gz
$pip install --no-index --find-links PATH pytest-nice
Usage
------::
$pytest -nice
插件编写好后,便可以执行命令安装:
(venv) E:\Programs\Python\Python_Pytest>pip install pytest-nice/
Processing e:\programs\python\python_pytest\pytest-nice
Requirement already satisfied: pytest in c:\python37\lib\site-packages (from pytest-nice==0.1.0)(4.5.0)
Requirement already satisfied: colorama; sys_platform =="win32"in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(0.4.1)
Requirement already satisfied: py>=1.5.0in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(1.8.0)
Requirement already satisfied: six>=1.10.0in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(1.12.0)
Requirement already satisfied: attrs>=17.4.0in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(19.1.0)
Requirement already satisfied: wcwidth in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(0.1.7)
Requirement already satisfied: setuptools in c:\users\davieyang\appdata\roaming\python\python37\site-packages (from pytest->pytest-nice==0.1.0)(41.0.1)
Requirement already satisfied: more-itertools>=4.0.0; python_version >"2.7"in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(7.0.0)
Requirement already satisfied: pluggy!=0.10,<1.0,>=0.9in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(0.11.0)
Requirement already satisfied: atomicwrites>=1.0in c:\python37\lib\site-packages (from pytest->pytest-nice==0.1.0)(1.3.0)
Building wheels for collected packages: pytest-nice
Building wheel for pytest-nice (setup.py)... done
Stored in directory: C:\Users\davieyang\AppData\Local\pip\Cache\wheels\db\f5\74\45aa97afa6c4141a69713b4b412273cbceaa0bb48770dd00d9
Successfully built pytest-nice
Installing collected packages: pytest-nice
Successfully installed pytest-nice-0.1.0
测试插件
既可以使用前边的用的方式,直接编写测试函数执行,在结果中查看插件是否运行正确,也可以使用一个名为pytester的插件来自动完成同样的工作,这个插件是pytest自带的,但默认是关闭的。
在test目录里的conftest.py文件中添加如下代码,即可开启pytester
"""pytester is needed for testing plugins."""
pytest_plugins ='pytester'
如此便开启了pytester,我们要使用的fixture叫做testdir,开启pytester之后就可以使用了。
"""Testing the pytest-nice plugin."""import pytest
deftest_pass_fail(testdir):# create a temporary pytest test module
testdir.makepyfile("""
def test_pass():
assert 1 == 1
def test_fail():
assert 1 == 2
""")# run pytest
result = testdir.runpytest()# fnmatch_lines does an assertion internally
result.stdout.fnmatch_lines(['*.F*',# . for Pass, F for Fail])# make sure that that we get a '1' exit code for the testsuiteassert result.ret ==1
testdir自动创建了一个临时的目录用来互访测试文件,他有一个名为makepyfile()的方法,允许我们写入测试文件
使用testdir.runpytest()方法来运行pytest并测试新的测试文件,还可以设置结果选项,返回值类型是RunResult
在该文件中新增测试方法:
@pytest.fixture()defsample_test(testdir):
testdir.makepyfile("""
def test_pass():
assert 1 == 1
def test_fail():
assert 1 == 2
""")return testdir
deftest_with_nice(sample_test):
result = sample_test.runpytest('--nice')
result.stdout.fnmatch_lines(['*.O*',])# . for Pass, O for Failassert result.ret ==1deftest_with_nice_verbose(sample_test):
result = sample_test.runpytest('-v','--nice')
result.stdout.fnmatch_lines(['*::test_fail OPPORTUNITY for improvement*',])assert result.ret ==1deftest_not_nice_verbose(sample_test):
result = sample_test.runpytest('-v')
result.stdout.fnmatch_lines(['*::test_fail FAILED*'])assert result.ret ==1deftest_header(sample_test):
result = sample_test.runpytest('--nice')
result.stdout.fnmatch_lines(['Thanks for running the tests.'])deftest_header_not_nice(sample_test):
result = sample_test.runpytest()
thanks_message ='Thanks for running the tests.'assert thanks_message notin result.stdout.str()deftest_help_message(testdir):
result = testdir.runpytest('--help')# fnmatch_lines does an assertion internally
result.stdout.fnmatch_lines(['nice:','*--nice*nice: turn FAILED into OPPORTUNITY for improvement',])
命令行执行该用例:
(venv) E:\Programs\Python\Python_Pytest\pytest-nice\tests>pytest -v
===================================== test session starts ===================================================
platform win32 -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0-- c:\python37\python.exe
cachedir:.pytest_cache
rootdir: E:\Programs\Python\Python_Pytest\pytest-nice
plugins: xdist-1.29.0, timeout-1.3.3, repeat-0.8.0, nice-0.1.0, instafail-0.4.1, forked-1.0.2, emoji-0.2.0, allure-pytest-2.6.3
collected 7 items
test_nice.py::test_pass_fail PASSED [14%]
test_nice.py::test_with_nice PASSED [28%]
test_nice.py::test_with_nice_verbose PASSED [42%]
test_nice.py::test_not_nice_verbose PASSED [57%]
test_nice.py::test_header PASSED [71%]
test_nice.py::test_header_not_nice PASSED [85%]
test_nice.py::test_help_message PASSED [100%]
创建发布包
执行命令:python setup.py sdist,结果如下:
(venv) E:\Programs\Python\Python_Pytest\pytest-nice>python setup.py sdist
running sdist
running egg_info
creating pytest_nice.egg-info
writing pytest_nice.egg-info\PKG-INFO
writing dependency_links to pytest_nice.egg-info\dependency_links.txt
writing entry points to pytest_nice.egg-info\entry_points.txt
writing requirements to pytest_nice.egg-info\requires.txt
writing top-level names to pytest_nice.egg-info\top_level.txt
writing manifest file'pytest_nice.egg-info\SOURCES.txt'
reading manifest file'pytest_nice.egg-info\SOURCES.txt'
writing manifest file'pytest_nice.egg-info\SOURCES.txt'
running check
creating pytest-nice-0.1.0
creating pytest-nice-0.1.0\pytest_nice.egg-info
copying files to pytest-nice-0.1.0...
copying README.rst -> pytest-nice-0.1.0
copying pytest_nice.py -> pytest-nice-0.1.0
copying setup.py -> pytest-nice-0.1.0
copying pytest_nice.egg-info\PKG-INFO -> pytest-nice-0.1.0\pytest_nice.egg-info
copying pytest_nice.egg-info\SOURCES.txt -> pytest-nice-0.1.0\pytest_nice.egg-info
copying pytest_nice.egg-info\dependency_links.txt -> pytest-nice-0.1.0\pytest_nice.egg-info
copying pytest_nice.egg-info\entry_points.txt -> pytest-nice-0.1.0\pytest_nice.egg-info
copying pytest_nice.egg-info\requires.txt -> pytest-nice-0.1.0\pytest_nice.egg-info
copying pytest_nice.egg-info\top_level.txt -> pytest-nice-0.1.0\pytest_nice.egg-info
Writing pytest-nice-0.1.0\setup.cfg
creating dist
Creating tar archive
removing 'pytest-nice-0.1.0'(and everything under it)
工程结构中便会自动生成如下两个文件夹
dist目录下的xxx.tar.gz是用来安装的安装文件,可以在任何可安装的地方安装包括当前目录,更方便用于共享。
通过共享目录分发插件
pip支持从共享目录安装,选择一个目录,将xxx.tar.gz文件放进去,例如放在myplugins目录里
然后执行命令:
pip install --no-index --find-links myplugins pytest-nice
--no-index
告诉pip不要查找PyPI
--find-links myplugins
告诉pip在myplugins中查找要安装的软件包
如果你更新了插件,则可以使用–upgrade选项更新安装
pip install --upgrade --no-index --find-links myplugins pytest-nice
通过PyPI发布插件
可以通过官方地址distributing查询更详细的通过PyPI发布插件的方法
除了python官方地址,还有个非常好的分享插件的地方是cookiecutter,cookiecutter
pip install cookiecutter
cookiecutter https://github.com/pytest-dev/cookiecutter-pytest-plugin
版权归原作者 Davieyang.D.Y 所有, 如有侵权,请联系我们删除。