æ»ã 次㞠
ããã«äœ¿ãã匷åãªpytestã¯ããã©ã°ã€ã³ã®ããã¯ã¹ã远å ãããšããã«è¯ããªããŸãã pytestã³ãŒãããŒã¹ã¯èšå®ãšæ¡åŒµæ©èœã§æ§æãããŠããããã©ã°ã€ã³ãä»ããŠå€æŽããã³æ¹åã§ããããã¯ããããŸãã

ãã®æ¬ã®äŸã¯ãPython 3.6ãšpytest 3.2ã䜿çšããŠæžãããŠããŸãã pytest 3.2ã¯ãPython 2.6ã2.7ãããã³Python 3.3+ããµããŒãããŠããŸãã
Tasksãããžã§ã¯ãã®ãœãŒã¹ã³ãŒããšããã®æ¬ã«ç€ºãããŠãããã¹ãŠã®ãã¹ãã®ãœãŒã¹ã³ãŒãã¯ã pragprog.comã«ããæ¬ã®WebããŒãžã®ãªã³ã¯ããå
¥æã§ããŸãã ãã¹ãã³ãŒããçè§£ããããã«ãœãŒã¹ã³ãŒããããŠã³ããŒãããå¿
èŠã¯ãããŸããã ãã¹ãã³ãŒãã¯ãäŸã§ã¯äŸ¿å©ãªåœ¢åŒã§ç€ºãããŠããŸãã ãã ãããããžã§ã¯ãã®ã¿ã¹ã¯ãå®è¡ãããããã¹ããµã³ãã«ã調æŽããŠèªåã®ãããžã§ã¯ãããã¹ããããããã«ã¯ïŒæãçžã£ãŠããªãïŒïŒãæžç±ã®WebããŒãžã«ã¢ã¯ã»ã¹ããŠäœåãããŠã³ããŒãããå¿
èŠããããŸãã æžç±ã®WebããŒãžã«ã¯ã æ£èª€è¡šã®ã¡ãã»ãŒãžãžã®ãªã³ã¯ãšãã£ã¹ã«ãã·ã§ã³ãã©ãŒã©ã ããããŸãã
ãã¿ãã¬ã®äžã«ã¯ããã®ã·ãªãŒãºã®èšäºã®ãªã¹ãããããŸãã
ããã«é²ããŸãããïŒ
ãã®æ¬ã®åã®ç« ã§äœæ¥ããå Žåããã§ã«ããã€ãã®ãã©ã°ã€ã³ãäœæããŠããããšã«é©ããããããŸããã ãããžã§ã¯ãã®ãããã¬ãã«ã®conftest.py
ãã¡ã€ã«ã«ãã£ã¯ã¹ãã£ãããã¯é¢æ°ãé
眮ããconftest.py
ãããŒã«ã«ã®conftest
ãã©ã°ã€ã³ãäœæããŸãã ãããã®conftest.py
ãã¡ã€ã«ãããããžã§ã¯ãéãä»ã®äººããŸãã¯äžçãšå
±æã§ããã€ã³ã¹ããŒã«å¯èœãªãã©ã°ã€ã³ã«å€æããã®ã¯ã»ãã®å°ãã®è¿œå äœæ¥ã§ãã
ãã®ç« ã¯ããµãŒãããŒãã£ã®ãã©ã°ã€ã³ãæ¢ãå Žæã®è³ªåã«çããããšããå§ããŸãã ããªãã®æ°ã®ãã©ã°ã€ã³ãå©çšå¯èœã§ããããã誰ããpytest
è¡ããã倿Žããã§ã«æžããŠããå¯èœæ§ãããªããããŸãã ç§ãã¡ã¯ãªãŒãã³ãœãŒã¹ã®ãã©ã°ã€ã³ãæ€èšããã®ã§ããã©ã°ã€ã³ãã»ãŒããªããããããããšãããŠããããããã§ãªãå Žåã¯ããã©ã°ã€ã³ãéçºããããåç
§ãšããŠäœ¿çšããŠç¬èªã®ãã©ã°ã€ã³ãäœæã§ããŸãã ãã®ç« ã¯ç¬èªã®ãã©ã°ã€ã³ã®äœæã«é¢ãããã®ã§ãããä»é²3ã Sampler Packãã©ã°ã€ã³ïŒ163ããŒãžïŒãå«ãŸããŠãããå¯èœãªããšã®æŠèŠã説æããŠããŸãã
ãã®ç« ã§ã¯ããã©ã°ã€ã³ã®äœææ¹æ³ãåŠã³ããã¹ããããã±ãŒãžã³ã°ãé
åžã®æ£ããæ¹åã瀺ããŸãã Pythonã®ããã±ãŒãžãšé
åžã«é¢ããå®å
šãªãããã¯ã¯åºç¯ã§ãããç¬èªã®æ¬ã§ãããšäž»åŒµããŠããããããã¹ãŠãç¶²çŸ
ããããã§ã¯ãããŸããã ããããããªãã¯ããªãã®ããŒã ãšãã©ã°ã€ã³ã亀æããããšãã§ããã®ã«ååãªæ
å ±ãåŸãŸãã ãŸããPyPIãµããŒããšæå°éã®äœæ¥ã§ãã©ã°ã€ã³ãäœæããããã€ãã®ç°¡åãªæ¹æ³ã«ã€ããŠã説æããŸãã
æ€çŽ¢ãã©ã°ã€ã³
ãµãŒãããŒãã£ã®pytestãã©ã°ã€ã³ã¯ããã€ãã®å Žæã«ãããŸãã ä»é²3ããã©ã°ã€ã³ãµã³ãã©ããã¯ïŒ163ããŒãžïŒã«ãªã¹ããããŠãããã©ââã°ã€ã³ã¯ãPyPIããããŠã³ããŒãã§ããŸãã ãã ãããããçŽ æŽãããpytestãã©ã°ã€ã³ãèŠã€ããå¯äžã®å Žæã§ã¯ãããŸããã
https://docs.pytest.org/en/latest/plugins.html
ã¡ã€ã³ã®pytestããã¥ã¡ã³ããµã€ãã«ã¯ãpytestãã©ã°ã€ã³ã®ã€ã³ã¹ããŒã«ãšäœ¿çšã«ã€ããŠèª¬æããããã€ãã®äžè¬çãªãã©ã°ã€ã³ããªã¹ãããããŒãžããããŸãã
https://pypi.python.org
Python Package IndexïŒPyPIïŒã¯ãå€ãã®Pythonããã±ãŒãžãå
¥æããã®ã«æé©ãªå Žæã§ãããpytestãã©ã°ã€ã³ãæ€çŽ¢ããã®ã«ãæé©ã§ãã ã»ãšãã©ã®pytestãã©ã°ã€ã³ã¯ãpytest-ãã§å§ãŸããã-pytestãã§çµãããããpytestãã©ã°ã€ã³ãæ€çŽ¢ãããšãã¯ãæ€çŽ¢ãã£ãŒã«ãã«ãpytestãããpytest-ããŸãã¯ã-pytestããšå
¥åããŸãã
https://github.com/pytest-dev
GitHubã®pytest-devã°ã«ãŒãã«ã¯ãpytestã®ãœãŒã¹ã³ãŒããä¿åãããŠããŸãã ããã«ãããã§ã¯ãpytestã«ãŒãã«ããŒã ãé·æçã«ãµããŒãããå¿
èŠããã人æ°ã®ããpytestãã©ã°ã€ã³ãèŠã€ããããšãã§ããŸãã
ãã©ã°ã€ã³ã®ã€ã³ã¹ããŒã«
Pytestãã©ã°ã€ã³ã¯ãä»ã®Pythonããã±ãŒãžãšåæ§ã«ãpipãšãšãã«ã€ã³ã¹ããŒã«ãããŸãã ããã
ãã©ã°ã€ã³ãã€ã³ã¹ããŒã«ããã«ã¯ãããã€ãã®æ¹æ³ã§pipã䜿çšã§ããŸãã
PyPIããã€ã³ã¹ããŒã«ãã
PyPIã¯pipã®ããã©ã«ãã®å Žæã§ãããããPyPIãããã©ã°ã€ã³ãã€ã³ã¹ããŒã«ããã®ãæãç°¡åãªæ¹æ³ã§ãã pytest-cov
ãã©ã°ã€ã³ãã€ã³ã¹ããŒã«ããŸãããã
$ pip install pytest-cov
PyPIã®ææ°ã®å®å®ããŒãžã§ã³ãã€ã³ã¹ããŒã«ãããŸãã
PyPIããç¹å®ã®ããŒãžã§ã³ãã€ã³ã¹ããŒã«ãã
ãã©ã°ã€ã³ã®ç¹å®ã®ããŒãžã§ã³ãå¿
èŠãªå Žåã¯ã ==
åŸã«ããŒãžã§ã³ãæå®ã§ããŸãã
$ pip install pytest-cov==2.4.0
.tar.gzãŸãã¯.whlãã¡ã€ã«ããã€ã³ã¹ããŒã«ãã
PyPIããã±ãŒãžã¯ãæ¡åŒµåã.tar.gz
ãŸãã¯.whl
zipãã¡ã€ã«ãšããŠé
åžãããŸãã 圌ãã¯ãã°ãã°ãã¿ãŒã«ããŒã«ããšããã€ãŒã«ããšåŒã°ããŸãã PyPIãçŽæ¥æäœããããšãããšåé¡ãçºçããå ŽåïŒãã¡ã€ã¢ãŠã©ãŒã«ããã®ä»ã®ãããã¯ãŒã¯ã®è€éãª.whl
ã§çºçããå¯èœæ§ããããŸãïŒ .whl
ãŸãã¯.whl
ãããŠã³ããŒãããŠã .whl
ããã€ã³ã¹ããŒã«ã§ããŸãã
ã¿ã³ããªã³ã§é梱ããããã³ã¹ãããããå¿
èŠã¯ãããŸããã ããããåããã ãã§ãïŒ
$ pip install pytest-cov-2.4.0.tar.gz # or $ pip install pytest_cov-2.4.0-py2.py3-none-any.whl
ããŒã«ã«ãã£ã¬ã¯ããªããã®ã€ã³ã¹ããŒã«
.tar.gz
ãŸãã¯.whl
ããŒã«ã«ãŸãã¯å
±æãã£ã¬ã¯ããªã«ãã©ã°ã€ã³ïŒããã³ä»ã®Pythonããã±ãŒãžïŒçšã®ãã©ã°ã€ã³ãçšæããPyPIã®ä»£ããã«ããã䜿çšããŠãã©ã°ã€ã³ãã€ã³ã¹ããŒã«ã§ããŸãã
$ mkdir some_plugins $ cp pytest_cov-2.4.0-py2.py3-none-any.whl some_plugins/ $ pip install --no-index --find-links=./some_plugins/ pytest-cov
--no-index
ã¯ãPipã«PyPIã«æ¥ç¶ããªãããã«æç€ºããŸãã --find-links=./some_plugins/
ã¯ãpipã«some_plugins
ãã£ã¬ã¯ããªã調ã¹ãããã«æç€ºããŸãã ãã®æ¹æ³ã¯ããµãŒãããŒãã£ãšãã€ãã£ãã®äž¡æ¹ã®ãã©ã°ã€ã³ãããŒã«ã«ã«ä¿åãããŠããå Žåãããã³ç¶ç¶ççµ±åãŸãã¯toxã䜿çšããŠæ°ããä»®æ³ç°å¢ãäœæããŠããå Žåã«ç¹ã«äŸ¿å©ã§ãã ïŒToxãšç¶ç¶çã€ã³ãã°ã¬ãŒã·ã§ã³ã®äž¡æ¹ã«ã€ããŠã¯ã第7ç« ã§pytestãä»ã®ããŒã«ãšäœ¿çšããŠã125ããŒãžã§èª¬æããŸããïŒ
ããŒã«ã«ãã£ã¬ã¯ããªã®ã€ã³ã¹ããŒã«æ¹æ³ã䜿çšãããšã==ãšããŒãžã§ã³çªå·ã远å ããŠãè€æ°ã®ããŒãžã§ã³ãã€ã³ã¹ããŒã«ããå¿
èŠãªããŒãžã§ã³ãæå®ã§ããããšã«æ³šæããŠãã ããã
$ pip install --no-index --find-links=./some_plugins/ pytest-cov==2.4.0
Gitãªããžããªããã€ã³ã¹ããŒã«ãã
ãã®å Žåã¯GitHubã®Gitãªããžããªãããã©ã°ã€ã³ãçŽæ¥ã€ã³ã¹ããŒã«ã§ããŸãã
$ pip install git+https://github.com/pytest-dev/pytest-cov
ããŒãžã§ã³ã¿ã°ãæå®ããããšãã§ããŸãã
$ pip install git+https://github.com/pytest-dev/pytest-cov@v2.4.0
ãŸãã¯ããã©ã³ããæå®ã§ããŸãïŒ
$ pip install git+https://github.com/pytest-dev/pytest-cov@master
Gitãªããžããªããã®ã€ã³ã¹ããŒã«ã¯ãGitã«ç¬èªã®äœæ¥ãä¿åããŠããå ŽåããŸãã¯ãã©ã°ã€ã³ãŸãã¯ãã©ã°ã€ã³ã®å¿
èŠãªããŒãžã§ã³ãPyPIã«ãªãå Žåã«ç¹ã«äŸ¿å©ã§ãã
翻蚳è
泚ïŒ
pipã¯GitãMercurialãSubversionãããã³Bazaarããã®ã€ã³ã¹ããŒã«ããµããŒãããURLãã¬ãã£ãã¯ã¹ã䜿çšããŠVCSã¿ã€ããå®çŸ©ããŸãïŒãgit +ãããhg +ãããsvn +ãããbzr +ãã
詳现ã«ã€ããŠã¯ã PyPIã®ããã¥ã¡ã³ããåç
§ããŠãã ããã
ç¬èªã®ãã©ã°ã€ã³ãæžã
å€ãã®ãµãŒãããŒãã£ãã©ã°ã€ã³ã«ã¯ãéåžžã«å€ãã®ã³ãŒããå«ãŸããŠããŸãã ããã¯ãããããã¹ãŠãèªåã§éçºããæéãç¯çŽããããã«äœ¿çšããçç±ã®1ã€ã§ãã ãã ããç¹å®ã®ã³ãŒãã«ã€ããŠã¯ããã¹ãã«åœ¹ç«ã€ç¹å¥ãªãã£ã¯ã¹ãã£ãšä¿®æ£ãééããªãæãä»ããŸãã ãã©ã°ã€ã³ãäœæããããšã«ãããè€æ°ã®ãããžã§ã¯ãéã§å
±æãããè€æ°ã®ãã£ã¯ã¹ãã£ãç°¡åã«å
±æã§ããŸãã ç¬èªã®ãã©ã°ã€ã³ãéçºããã³é
åžããããšã«ããããããã®å€æŽãããã€ãã®ãããžã§ã¯ãïŒå Žåã«ãã£ãŠã¯ä»ã®å°åïŒãšå
±æã§ããŸãã ããã¯éåžžã«ç°¡åã§ãã ãã®ã»ã¯ã·ã§ã³ã§ã¯ãpytestã®åäœã®å°ããªå€æŽãéçºãããã©ã°ã€ã³ãšããŠããã¯ãããã¹ãããŠãé
åžæ¹æ³ãæ€èšããŸãã
ãã©ã°ã€ã³ã«ã¯ãpytestã®åäœã倿Žããããã¯é¢æ°ãå«ããããšãã§ããŸãã pytestã¯ãã©ã°ã€ã³ãpytestã®åäœããããã«å€æŽã§ããããã«èšèšãããŠãããããå€ãã®ããã¯é¢æ°ãå©çšå¯èœã§ãã pytestã®ããã¯ã¯ãpytestããã¥ã¡ã³ããµã€ãã«ãªã¹ããããŠããŸã ã ãã®äŸã§ã¯ããã¹ãã¹ããŒã¿ã¹ã®å€èгã倿Žãããã©ã°ã€ã³ãäœæããŸãã ã³ãã³ãã©ã€ã³ãã©ã¡ãŒã¿ãŒã远å ããŠããã®æ°ããåäœãæå¹ã«ããŸãã åºåããããŒã«ããã¹ãã远å ããŸãã ç¹ã«ããã¹ãŠã®FAILEDã¹ããŒã¿ã¹ã€ã³ãžã±ãŒã¿ïŒå€±æïŒããæ¹åã®ããã«OPPORTUNITYïŒèŠèŸŒã¿ïŒãã«å€æŽããFãOã«å€æŽããããã¹ããå®è¡ããŠããã ãããããšãããããŸãããããããŒã«è¿œå ããŸãã ãããè¡ãã«ã¯ã --nice
ãªãã·ã§ã³ã䜿çšããŸãã
åäœã®å€æŽããã©ã°ã€ã³ã®ã¡ã«ããºã ã®èª¬æãšã¯å¥ã«ããããã«ãconftest.pyãåé
åžå¯èœãªãã©ã°ã€ã³ã«å€ããåã«å€æŽãå ããŸãã ãã®æ¹æ³ã§ãã©ã°ã€ã³ãå®è¡ããå¿
èŠã¯ãããŸããã ããããå€ãã®å Žåã1ã€ã®ãããžã§ã¯ãã§ã®ã¿äœ¿çšããããšãæå³ãã倿Žã¯ãå
±æããŠãã©ã°ã€ã³ã«å€æããã®ã«ååã«åœ¹ç«ã¡ãŸãã ãããã£ãŠãconftest.pyãã¡ã€ã«ã«æ©èœã远å ããããšããå§ããconftest.pyã§ãã¹ãŠãæ©èœããåŸãã³ãŒããããã±ãŒãžã«ç§»åããŸãã
ã¿ã¹ã¯ãããžã§ã¯ãã«æ»ããŸãã 30ããŒãžã®ãäŸå€ã®åŸ
æ©ãã»ã¯ã·ã§ã³ã§ã¯ã誰ããAPI颿°ã誀ã£ãŠåŒã³åºããå Žåã«äŸå€ãã¹ããŒããããã©ããã確èªããããã€ãã®ãã¹ããäœæããŸããã å°ãªããšãããã€ãã®èãããããšã©ãŒç¶æ
ãèŠéããããã§ãã
ããã«ããã€ãã®ãã¹ãã瀺ããŸãã
ch5 / a / tasks_proj / tests / func / test_api_exceptions.py
""" API wrong.""" import pytest import tasks from tasks import Task @pytest.mark.usefixtures('tasks_db') class TestAdd(): """, tasks.add().""" def test_missing_summary(self): """ , summary missing.""" with pytest.raises(ValueError): tasks.add(Task(owner='bob')) def test_done_not_bool(self): """ , done bool.""" with pytest.raises(ValueError): tasks.add(Task(summary='summary', done='True'))
ããããå®è¡ããŠãåæ Œãããã©ããã確èªããŸãããã
$ cd /path/to/code/ch5/a/tasks_proj $ pytest ===================== test session starts ====================== collected 57 items tests/func/test_add.py ... tests/func/test_add_variety.py ............................ tests/func/test_add_variety2.py ............ tests/func/test_api_exceptions.py .F....... tests/func/test_unique_id.py . tests/unit/test_task.py .... =========================== FAILURES =========================== __________________ TestAdd.test_done_not_bool __________________ self = <func.test_api_exceptions.TestAdd object at 0x103a71a20> def test_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'> tests/func/test_api_exceptions.py:20: Failed ============= 1 failed, 56 passed in 0.28 seconds ==============
詳现ã«ã€ããŠã¯ã -v
ã§å床å®è¡ããŠã¿ãŸãããã ãã¬ãŒã¹ã¯ãã§ã«è¡šç€ºãããŠããã®ã§ã-- --tb=no
æŒããŠ--tb=no
ãªãã«ã§ããŸãã
-k TestAdd
ã䜿çšããæ°ãããã¹ãã«çŠç¹ãåœãŠãŸããããããã¯ããTestAddããå«ãååã®ãã¹ããä»ã«ãªãããæ©èœããŸãã
ããã¹ãŠãèœãšããŠããã®ãã¹ãã®ä¿®æ£ã詊ã¿ãããšãã§ããŸãïŒãããŠãåŸã§ãã¹ããè¡ããŸãïŒããä»åºŠã¯éçºè
ã«ãšã£ãŠå€±æãããå¿«é©ã«ããããšã«çŠç¹ãåœãŠãŸãã
ãŸããããããŒã«ãããããšãããšããã¡ãã»ãŒãžã远å ããŸããããã¯ã pytest_report_header()
ãšããpytest_report_header()
ããã¯ã䜿çšããŠå®è¡ã§ããŸãã
ch5 / b / tasks_proj / tests / conftest.py
def pytest_report_header(): """ .""" return "Thanks for running the tests."
ã瀌ã®ã¡ã¢ãå
¥åããã®ã¯æããã«æãã§ãã ãã ããããããŒã«æ
å ±ã远å ããæ©èœã¯æ¡åŒµã§ããŸãã ãŠãŒã¶ãŒåã远å ãã䜿çšããæ©åšãšãã¹ãããããŒãžã§ã³ãæå®ã§ããŸãã äžè¬ã«ãæååã«å€æã§ãããã®ã¯ãã¹ãŠãã¹ãã®ã¿ã€ãã«ã«æ¿å
¥ã§ããŸãã
次ã«ããã¹ãã¹ããŒã¿ã¹ã¬ããŒãã倿ŽããŠã F
ãO
ã FAILED
ãOPPORTUNITY for improvement
ãŸãã ãã®ããšãå¯èœã«ããããã¯ããããŸãïŒ pytest_report_teststatus()
ïŒ
ch5 / b / tasks_proj / tests / conftest.py
def pytest_report_teststatus(report): """ .""" if report.when == 'call' and report.failed: return (report.outcome, 'O', 'OPPORTUNITY for improvement')
ãããŠä»ãç§ãã¡ã¯æ¢ããŠãããœãªã¥ãŒã·ã§ã³ãæã«å
¥ããŸããã --verbose
ãã©ã°ã䜿çšããªããã¹ãã»ãã·ã§ã³ã§ã¯ã倱æã«å¯ŸããŠO
ã衚瀺ãããŸããã€ãŸããæ¹åãå¯èœã§ãã
$ cd /path/to/code/ch5/b/tasks_proj/tests/func $ pytest --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests. collected 9 items test_api_exceptions.py .O ====================== 7 tests deselected ====================== ======= 1 failed, 1 passed, 7 deselected in 0.06 seconds =======
-v
ãŸãã¯--verbose
ãã©ã°ã䜿çšããæ¹ãè¯ãã§ãããïŒ
$ pytest -v --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests. collected 9 items test_api_exceptions.py::TestAdd::test_missing_summary PASSED test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement ====================== 7 tests deselected ====================== ======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======
æåŸã«è¡ã倿Žã¯ãã³ãã³ãã©ã€ã³ãã©ã¡ãŒã¿ãŒ--nice,
ã远å ããããšã§ã--nice,
ããã«ãããã¹ããŒã¿ã¹ã®å€æŽã¯ã-- --nice
ã眮ãæããå Žåã«ã®ã¿çºçããŸãã
def pytest_addoption(parser): """ nice --nice.""" group = parser.getgroup('nice') group.addoption("--nice", action="store_true", help="nice: turn failures into opportunities") def pytest_report_header(): """ .""" if pytest.config.getoption('nice'): return "Thanks for running the tests." def pytest_report_teststatus(report): """ .""" if report.when == 'call': if report.failed and pytest.config.getoption('nice'): return (report.outcome, 'O', 'OPPORTUNITY for improvement')
ãã®ãã©ã°ã€ã³ã§ã¯ãããã€ãã®ããã¯ãã䜿çšããªãããšã«æ³šæããŠãã ããã ã¡ã€ã³ã®Pytestããã¥ã¡ã³ããµã€ãã«ã¯ãä»ã«ã倿°ã®ãµã€ãããããŸãã
ããã§ãäŸã§ãã©ã°ã€ã³ãå®è¡ããã ãã§ããã©ã°ã€ã³ãæåã§ãã¹ãã§ããŸãã æåã«ã --nice
ãªãã·ã§ã³ã䜿çšããã«ããŠãŒã¶ãŒåã®ã¿ã衚瀺ãããããã«ããŸãã
$ cd /path/to/code/ch5/c/tasks_proj/tests/func $ pytest --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== collected 9 items test_api_exceptions.py .F ====================== 7 tests deselected ====================== ======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======
--nice
ïŒ
$ pytest --nice --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests. collected 9 items test_api_exceptions.py .O ====================== 7 tests deselected ====================== ======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======
--nice
ãš--verbose
ïŒ
$ pytest -v --nice --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests. collected 9 items test_api_exceptions.py::TestAdd::test_missing_summary PASSED test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement ====================== 7 tests deselected ====================== ======= 1 failed, 1 passed, 7 deselected in 0.06 seconds =======
ãããïŒ conftest.py
ãã¡ã€ã«ã®çŽ10è¡ã®ã³ãŒãã§ã conftest.py
倿Žããã¹ãŠè¡ããŸããã æ¬¡ã«ããã®ã³ãŒãããã©ã°ã€ã³æ§é ã«ç§»åããŸãã
ã€ã³ã¹ããŒã«å¯èœãªãã©ã°ã€ã³ãäœæãã
ãã©ã°ã€ã³ãä»ã®ãŠãŒã¶ãŒãšå
±æããããã»ã¹ã¯æç¢ºã«å®çŸ©ãããŠããŸãã ç¬èªã®ãã©ã°ã€ã³ãPyPIã«å«ããªãå Žåã§ãããã®ããã»ã¹ãå®è¡ãããšããªãŒãã³ãœãŒã¹ãã©ã°ã€ã³ããã³ãŒããèªã¿ããããªãããããã圹ç«ã€ãã©ãããè©äŸ¡ããæ©äŒãå¢ããŸãã
ãã®ãããã¯ã¯ä»ã®å Žæã§ååã«ææžåãããŠããããããã®æ¬ã§Pythonããã±ãŒãžã®ããã±ãŒãžåãšé
åžãå®å
šã«ã«ããŒããããšã¯äžèŠã§ãã ãããšãããšãã·ã¢èªã ãã ããåã®ã»ã¯ã·ã§ã³ã§äœæããããŒã«ã«æ§æãã©ã°ã€ã³ãããpipã䜿çšããŠã€ã³ã¹ããŒã«ããããã®ã«ç§»è¡ããããšã¯å€§ããããšã§ã¯ãããŸããã ã
æåã«ããã©ã°ã€ã³ã³ãŒãããã¹ãããæ°ãããã£ã¬ã¯ããªãäœæããå¿
èŠããããŸãã åŒã³æ¹ã¯é¢ä¿ãããŸãããããã€ã¹ãã©ã°çšã®ãã©ã°ã€ã³ãäœæããŠããã®ã§ãpytest-niceãšåŒã³ãŸãããã ãã®æ°ãããã£ã¬ã¯ããªã«ã¯ãpytest_nice.pyãšsetup.pyã®2ã€ã®ãã¡ã€ã«ããããŸãã ïŒãã¹ãã«ã¿ãã°ã«ã€ããŠã¯ã105ããŒãžã®ããã¹ããã©ã°ã€ã³ãã»ã¯ã·ã§ã³ã§èª¬æããŸããïŒ
â LICENSE â pytest_nice.py â setup.py â ââââtests â conftest.py â test_nice.py
pytest_nice.py
ããã®é¢æ°ã«é¢é£ä»ããããconftest.pyã®æ£ç¢ºãªã³ã³ãã³ããé
眮ããŸãïŒãããŠtasks_proj/tests/conftest.py
ããæœåºãtasks_proj/tests/conftest.py
ïŒïŒ
ch5 / pytest-nice / pytest_nice.py
""" pytest-nice .""" import pytest def pytest_addoption(parser): """ nice --nice.""" group = parser.getgroup('nice') group.addoption("--nice", action="store_true", help="nice: turn FAILED into OPPORTUNITY for improvement") def pytest_report_header(): """ .""" if pytest.config.getoption('nice'): return "Thanks for running the tests." def pytest_report_teststatus(report): """ .""" if report.when == 'call': if report.failed and pytest.config.getoption('nice'): return (report.outcome, 'O', 'OPPORTUNITY for improvement')
setup.py
æå°éã®setup()
åŒã³åºããå¿
èŠã§ãã
ch5 / pytest-nice / setup.py
"""Setup pytest-nice plugin.""" from setuptools import setup setup( name='pytest-nice', version='0.1.0', description=' Pytest, FAILURE into OPPORTUNITY', url='https:////////', author=' ', author_email='your_email@somewhere.com', license='proprietary', py_modules=['pytest_nice'], install_requires=['pytest'], entry_points={'pytest11': ['nice = pytest_nice', ], }, )
å¹
åºããªãŒãã£ãšã³ã¹ãŸãã¯ã€ã³ã¿ãŒãããã«é
ä¿¡ããå Žåã¯ãèšå®ã§ããã«æ
å ±ãå¿
èŠã«ãªããŸãã ãã ããå°ããªããŒã ã®å Žåãèªåèªèº«ã®å Žåã¯ããã§ååã§ãã
setup()
ä»ã®ãã©ã¡ãŒã¿ãŒãå«ããããšãã§ããŸãã ããã«ã¯å¿
é ãã£ãŒã«ãã®ã¿ããããŸãã ããŒãžã§ã³ãã£ãŒã«ãã¯ããã®ãã©ã°ã€ã³ã®ããŒãžã§ã³ã§ãã ãããŠãããªããããŒãžã§ã³ãç«ã¡äžãããšããããã¯å®å
šã«ããªã次第ã§ãã URLãã£ãŒã«ãã¯å¿
é ã§ãã 空çœã®ãŸãŸã«ã§ããŸãããèŠåã衚瀺ãããŸãã author_email
ãšauthor_email
ã¯ã maintainer
ãšmaintainer_email
眮ãæããããšãã§ããŸããããããã®ãã¢ã®ãããããååšããã¯ãã§ãã license
ãã£ãŒã«ãã¯çãããã¹ããã£ãŒã«ãã§ãã ããã¯ãå€ãã®ãªãŒãã³ãœãŒã¹ã©ã€ã»ã³ã¹ã®1ã€ãããªãã®ååãäŒç€ŸããŸãã¯ããªãã«é©ãããã®ã§ãã py_modules
ã¯ããã®ãã©ã°ã€ã³ã®å¯äžã®ã¢ãžã¥ãŒã«ãšããŠpy_modules
ãªã¹ããããŠããŸãã ããã¯ãªã¹ãã§ãããè€æ°ã®ã¢ãžã¥ãŒã«ãå«ããããšãã§ããŸãããè€æ°ããå Žåã¯ãããã±ãŒãžã䜿çšããŠãã¹ãŠã®ã¢ãžã¥ãŒã«ã1ã€ã®ãã£ã¬ã¯ããªã«é
眮ããŸãã
ãããŸã§ã®ãšããããã¹ãŠã®setup()
ãªãã·ã§ã³ã¯æšæºã§ããããã¹ãŠã®Pythonã€ã³ã¹ããŒã©ãŒã«ãã£ãŠäœ¿çšãããŸãã Pytestãã©ã°ã€ã³ã§ç°ãªãéšåã¯ã entry_points
ãã©ã¡ãŒã¿ãŒã§ãã entry_points={'pytest11': ['nice = pytest_nice', ], },.
ããªã¹ããentry_points={'pytest11': ['nice = pytest_nice', ], },.
entry_points
颿°ã¯setuptools
æšæºã§ãããpytest11ã¯pytestãæ¢ããŠããç¹å¥ãªèå¥åã§ãã ãã®è¡ã§ã¯ã nice
ã¯ãã©ã°ã€ã³ã®ååã§ããã pytest_nice
ãã©ã°ã€ã³ãååšããã¢ãžã¥ãŒã«pytest_nice
ååã§ããããšãpytestã«äŒããŸãã ããã±ãŒãžã䜿çšããå Žåãããã§ã®ãšã³ããªã¯æ¬¡ã®ããã«ãªããŸãã
README.rst
ãã¡ã€ã«ã«ã€ããŠã¯README.rst
話ããŠREADME.rst
ã äœããã®åœ¢åŒã®READMEãsetuptoolsã®èŠä»¶ã§ãã ã¹ããããããšããããåŸãããŸãïŒ
... warning: sdist: standard file not found: should have one of README, README.rst, README.txt ...
ãšã«ããããããžã§ã¯ãæ
å ±ãå«ããããã®æšæºçãªæ¹æ³ãšããŠREADMEãä¿æããããšã¯è¯ãèãã§ãã pytest-niceã®ãã¡ã€ã«ã«å
¥ãããã®ã¯æ¬¡ã®ãšããã§ãã
ch5 / pytest-nice / README.rst
pytest-nice : A pytest plugin ============================= pytest . -------- - , pytest. - ``--nice`` , : - ``F`` ``O`` - ``-v``, ``FAILURE`` ``OPPORTUNITY for improvement`` ------------ , Pytest .tar.gz PATH, : :: $ pip install PATH/pytest-nice-0.1.0.tar.gz $ pip install --no-index --find-links PATH pytest-nice ----- :: $ pytest --nice
READMEãã¡ã€ã«ã®å
容ã«ã€ããŠå€ãã®æèŠããããŸãã ããã¯å€§å¹
ã«ããªãã³ã°ãããããŒãžã§ã³ã§ãããåäœããŸãã
ãã©ã°ã€ã³ã®ãã¹ã
ãã©ã°ã€ã³ã¯ãä»ã®ã³ãŒããšåæ§ã«ãã¹ãããå¿
èŠãããã³ãŒãã§ãã ãã ãããã¹ãããŒã«ãžã®å€æŽã®ãã¹ãã¯ããå°ãè€éã§ãã 98ããŒãžã®ãç¬èªã®ãã©ã°ã€ã³ã®äœæãã»ã¯ã·ã§ã³ã§ãã©ã°ã€ã³ã³ãŒããéçºãããšãããµã³ãã«ãã¹ããã¡ã€ã«ã䜿çšããŠæåã§ãã§ãã¯ããpytestãå®è¡ããŠãåºåãæ£ããããšã確èªããŸããã pytester
ã«ä»å±ããŠãããããã©ã«ãã§ã¯ç¡å¹ã«ãªã£ãŠããpytesterãšãããã©ã°ã€ã³ã䜿çšããŠãèªåã¢ãŒãã§ãåãããšãã§ããŸãã
pytest-niceã®ãã¹ããã£ã¬ã¯ããªã«ã¯ãconftest.pyãštest_nice.pyã®2ã€ã®ãã¡ã€ã«ããããŸãã pytester
ã䜿çšããã«ã¯ã pytester
ã«1è¡ã ã远å ããå¿
èŠãããconftest.py
ã
ch5 / pytest-nice / tests / conftest.py
"""pytester is needed for testing plugins.""" pytest_plugins = 'pytester'
ãã®è¡ã«ã¯ã pytester
ãã©ã°ã€ã³ãå«ãŸããŠpytester
ãŸãã testdir
ãšãããã£ã¯ã¹ãã£ã䜿çšããŸãã pytester
ã pytester
ãªã³ã«ãªã£ããšãã«äœ¿çšå¯èœã«ãªããŸãã
å€ãã®å Žåããã©ã°ã€ã³ã®ãã¹ãã¯ãæåã§èª¬æãã圢åŒãåããŸãã
- ãµã³ãã«ã®ãã¹ããã¡ã€ã«ãäœæããŸãã
- ãµã³ãã«ãã¡ã€ã«ãå«ããã£ã¬ã¯ããªã§ããã€ãã®ãã©ã¡ãŒã¿ãŒã䜿çšããŠããŸãã¯äœ¿çšããã«pytestãå®è¡ããŸãã
- åºåã確èªããŠãã ããã
- å¯èœãªå Žåãçµæã³ãŒãããã§ãã¯ããã«ã¯ããã¹ãŠã®ãã¹ã§0ãäžéšã®å€±æã§1ã«ãªããŸãã
1ã€ã®äŸãèŠãŠã¿ãŸãããã
ch5 / pytest-nice / tests / test_nice.py
def test_pass_fail(testdir):
Fixture testdir
ã¯ããã¹ããã¡ã€ã«ããã¹ãããããã®äžæãã£ã¬ã¯ããªãèªåçã«äœæããŸãã makepyfile()
, . : , , .
pytest testdir.runpytest()
. , . RunResult .
stdout
ret
. , , , fnmatch_lines
, , , , ret
0 1 . , fnmatch_lines
, . . , , :
ch5/pytest-nice/tests/test_nice.py
@pytest.fixture() def sample_test(testdir): testdir.makepyfile(""" def test_pass(): assert 1 == 1 def test_fail(): assert 1 == 2 """) return testdir
, , sample_test
, . :
ch5/pytest-nice/tests/test_nice.py
def test_with_nice(sample_test): result = sample_test.runpytest('--nice') result.stdout.fnmatch_lines(['*.O', ])
. , :
ch5/pytest-nice/tests/test_nice.py
def test_header(sample_test): result = sample_test.runpytest('--nice') result.stdout.fnmatch_lines(['Thanks for running the tests.']) def test_header_not_nice(sample_test): result = sample_test.runpytest() thanks_message = 'Thanks for running the tests.' assert thanks_message not in result.stdout.str()
, , .
:
ch5/pytest-nice/tests/test_nice.py
def test_help_message(testdir): result = testdir.runpytest('--help')
, , , .
pytest-nice
, . .zip.gz
, :
$ cd /path/to/code/ch5/pytest-nice/ $ pip install . Processing /path/to/code/ch5/pytest-nice Requirement already satisfied: pytest in /path/to/venv/lib/python3.6/site-packages (from pytest-nice==0.1.0) Requirement already satisfied: py>=1.4.33 in /path/to/venv/lib/python3.6/site-packages (from pytest->pytest-nice==0.1.0) Requirement already satisfied: setuptools in /path/to/venv/lib/python3.6/site-packages (from pytest->pytest-nice==0.1.0) Building wheels for collected packages: pytest-nice Running setup.py bdist_wheel for pytest-nice ... done ... Successfully built pytest-nice Installing collected packages: pytest-nice Successfully installed pytest-nice-0.1.0
, , :
$ pytest -v ===================== test session starts ====================== plugins: nice-0.1.0 collected 7 items tests/test_nice.py::test_pass_fail PASSED tests/test_nice.py::test_with_nice PASSED tests/test_nice.py::test_with_nice_verbose PASSED tests/test_nice.py::test_not_nice_verbose PASSED tests/test_nice.py::test_header PASSED tests/test_nice.py::test_header_not_nice PASSED tests/test_nice.py::test_help_message PASSED =================== 7 passed in 0.34 seconds ===================
ãæ³šæ : , . .
platform win32 -- Python 3.6.5, pytest-3.9.3, py-1.7.0, pluggy-0.8.0 -- c:\venv36\scripts\python.exe collected 7 items tests/test_nice.py::test_pass_fail FAILED [ 14%] tests/test_nice.py::test_with_nice OPPORTUNITY for improvement [ 28%] tests/test_nice.py::test_with_nice_verbose OPPORTUNITY for improvement [ 42%] tests/test_nice.py::test_not_nice_verbose FAILED [ 57%] tests/test_nice.py::test_header PASSED [ 71%] tests/test_nice.py::test_header_not_nice PASSED [ 85%] tests/test_nice.py::test_help_message PASSED [100%] ================================== FAILURES =================================== _______________________________ test_pass_fail ________________________________
result.stdout.fnmatch_lines([ '*.F', # . for Pass, F for Fail ])
ã«
result.stdout.fnmatch_lines([ '*.F*', # . for Pass, F for Fail ])
*
F
test_with_nice
, test_with_nice_verbose
, test_not_nice_verbose
pytest.
c
'test_with_nice.py .O [100%]'
.
,
RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
!
(venv36) c:\_BOOKS_\pytest_si\bopytest-code\code\ch5\pytest-nice>pytest -v ============================= test session starts ============================= platform win32 -- Python 3.6.5, pytest-3.9.3, py-1.7.0, pluggy-0.8.0 -- c:\venv36\scripts\python.exe cachedir: .pytest_cache rootdir: c:\_BOOKS_\pytest_si\bopytest-code\code\ch5\pytest-nice, inifile: plugins: nice-0.1.0 collected 7 items tests/test_nice.py::test_pass_fail PASSED [ 14%] tests/test_nice.py::test_with_nice PASSED [ 28%] tests/test_nice.py::test_with_nice_verbose PASSED [ 42%] tests/test_nice.py::test_not_nice_verbose PASSED [ 57%] tests/test_nice.py::test_header PASSED [ 71%] tests/test_nice.py::test_header_not_nice PASSED [ 85%] tests/test_nice.py::test_help_message PASSED [100%] ============================== warnings summary =============================== tests/test_nice.py::test_pass_fail c:\venv36\lib\site-packages\_pytest\compat.py:332: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead return getattr(object, name, default)
ãã£ãïŒ . (pytest-nice), Python
pytest-:
$ pip uninstall pytest-nice Uninstalling pytest-nice-0.1.0: Would remove: \path\to\venv\lib\site-packages\pytest_nice-0.1.0.dist-info\* ... Proceed (y/n)? y Successfully uninstalled pytest-nice-0.1.0
â , pytest, PyPI.
, . , setup.py :
$ cd /path/to/code/ch5/pytest-nice $ python setup.py sdist running sdist running egg_info creating pytest_nice.egg-info ... running check creating pytest-nice-0.1.0 ... creating dist Creating tar archive ... $ ls dist pytest-nice-0.1.0.tar.gz
( , sdist source distribution â â .â)
pytest-nice dist pytest-nice-0.1.0.tar.gz
.
, , :
$ pip install dist/pytest-nice-0.1.0.tar.gz Processing ./dist/pytest-nice-0.1.0.tar.gz ... Installing collected packages: pytest-nice Successfully installed pytest-nice-0.1.0
.tar.gz
, .
pip
, , , , , , .tar.gz
. , pytest-nice-0.1.0.tar.gz
myplugins
.
pytest-nice
myplugins
:
$ pip install --no-index --find-links myplugins pytest-nice
--no-index
pip
PyPI, , .
The --find-links myplugins tells PyPI to look in myplugins for packages to install. And of course, pytest-nice is what we want to install.
--find-links myplugins
PyPI myplugins
. , pytest-nice
â , .
myplugins
, , --upgrade
:
$ pip install --upgrade --no-index --find-links myplugins pytest-nice
pip
, --no-index --find-links myplugins
.
PyPI
, , . , . , , , Python Packaging .
pytest, â cookiecutter-pytest-plugin
:
$ pip install cookiecutter $ cookiecutter https://github.com/pytest-dev/cookiecutter-pytest-plugin
. , . ; , , . pytest, , .
ch4/cache/test_slower.py
autouse fixture, check_duration()
. 4. .
- pytest-slower, , , « » . 102.
- , pytest-slower , .
- ãã©ã°ã€ã³ã®ãã¹ãã³ãŒããäœæããŸãã
- èŠãŠã¿ãŸãããPythonã®ããã±ãŒãžã€ã³ããã¯ã¹ããšÂ«pytest-Â»ãæ¢ããŸãããããããããªpytestãã©ã°ã€ã³ãèŠã€ããŠãã ããã
- éžæãããã©ã°ã€ã³ãã€ã³ã¹ããŒã«ããã¿ã¹ã¯ãã¹ãã§è©ŠããŠãã ããã
次ã¯äœã§ãã
conftest.py
ãã®æ¬ã§ã¯ãããŸã§äœåºŠã䜿çšããŸãããããšãã°ãpytestã®å®è¡ã«åœ±é¿ããèšå®ãã¡ã€ã«ããããŸãpytest.ini
ãæ¬¡ã®ç« ã§ã¯ãããŸããŸãªæ§æãã¡ã€ã«ã«ç²Ÿéãããã¹ãæã«çæŽ»ãæ¥œã«ããããã«ã§ããããšãèŠã€ããŸãã
æ»ã 次㞠