ããã¯ããªã«ãããããŠæžãããã®ïŒ
Pythonã§ãã¹ãã³ãŒããæžãæã«ã¯ãpytestã䜿ãã®ãè¯ããšãã話ãèããŸããŠã1床ãèªåã§ãæŒãããŠãããããªããšã
https://docs.pytest.org/en/latest/
以åãunittestã§èšèŒããå 容ã®pytestçã§ãã
unittestライブラリで、Pythonのテストコードを書いて実行する - CLOVER🍀
pytestãšã¯
ããããããã¹ãã£ã³ã°ãã¬ãŒã ã¯ãŒã¯ã§ãã
以äžãç¹åŸŽã ããã§ãã
- ã¢ãµãŒã·ã§ã³ã«å€±æããæã®è©³çŽ°æ å ±è¡šç€º
- ãã¹ãã¢ãžã¥ãŒã«ããã³é¢æ°ã®ãªãŒããã£ã¹ã«ããªãŒ
- å°ããªãã¹ããããã©ã¡ã¿ã©ã€ãºåãããé·æéã®ãã¹ãã®ããã®ãã¹ããªãœãŒã¹ã管çãããã¢ãžã¥ãŒã«åãããfixture
- unittestããã³noseã®ãã¹ãã±ãŒã¹ãå®è¡å¯èœ
- Python 3.5以äžãPyPy3äžã§åäœ
- ãªãããªãã©ã°ã€ã³ã¢ãŒããã¯ãã£ã«ããã315以äžã®å€éšãã©ã°ã€ã³ãšã³ãã¥ããã£
äžäœäºææ§ããPython 2.7ããã³3.4ã«é¢ãããµããŒãã«ã€ããŠã¯ããã¡ãã
Backwards Compatibility Policy — pytest documentation
Python 2.7 and 3.4 support — pytest documentation
APIããã¥ã¡ã³ãã«ã€ããŠã¯ããã¡ãã§ãã
API Reference — pytest documentation
ãŸãããšãããã䜿ã£ãŠãã£ãŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ python3 -V Python 3.6.9 $ pip3 -V pip 9.0.1 from /path/to/venv/lib/python3.6/site-packages (python 3.6)
ã€ã³ã¹ããŒã«
ãŸãã¯ãã€ã³ã¹ããŒã«ããŸãããã
Installation and Getting Started — pytest documentation
pip3ã§ã€ã³ã¹ããŒã«ã
$ pip3 install pytest
ä»å䜿çšããpytestã®ããŒãžã§ã³ã¯ã5.3.5ã§ãã
$ pip3 freeze | grep pytest pytest==5.3.5
ã¡ãªã¿ã«ããã®ããŒãžã«ãµããŒãããPythonã®ããŒãžã§ã³ãšãã©ãããã©ãŒã ãããããªãšæžãããŠããŸããã
ãã®ç°å¢äžã«ããã¹ã察象ããã³ãã¹ãã³ãŒããæžããŠãããŸãããã
ãé¡ãšãããžã§ã¯ãæ§æ
ãé¡ãšãããžã§ã¯ãæ§æããåºæ¬çã«ã¯unittestã®æã«æžãããã®ã«åãããŸãã
unittestライブラリで、Pythonのテストコードを書いて実行する - CLOVER🍀
ãŸãã¯ãã£ã¬ã¯ããªã¬ã€ã¢ãŠãã
Choosing a test layout / import rules
ãã¹ãã³ãŒãããã¹ã察象ïŒã¢ããªã±ãŒã·ã§ã³ã³ãŒãïŒåŽã«çœ®ãã¹ã¿ã€ã«ãšãããã§ãªãã¹ã¿ã€ã«ãããããã§ãããä»åã¯
ã¢ããªã±ãŒã·ã§ã³ã³ãŒããšãã¹ãã³ãŒããããããªåããŠçœ®ããããšæããŸãã
Tests outside application code
ã¢ããªã±ãŒã·ã§ã³ã³ãŒãã眮ããã£ã¬ã¯ããªããsampleãããã¹ãã³ãŒãã眮ããã£ã¬ã¯ããªããtestsããšããŸãã
$ mkdir sample tests
ã¢ããªã±ãŒã·ã§ã³ã³ãŒãåŽãã1床ãsrcããã£ã¬ã¯ããªã«çœ®ããšãã話ãããããã§ãããããã¯ãŸãå¥ã®æ©äŒã«èŠãŠã¿ãŸãããã
Packaging a python library | ionel's codelog
ã¢ããªã±ãŒã·ã§ã³ã³ãŒãããã³ããã¹ããäœæããŠå®è¡ããŠã¿ã
ã§ã¯ããŸãã¢ããªã±ãŒã·ã§ã³ã³ãŒããäœæã
sample/calc.py
class Calc: def add(self, x, y): return x + y def minus(self, x, y): return x - y def multiply(self, x, y): return x * y def divide(self, x, y): return x / y
ç¶ããŠããã¹ãã³ãŒããæžããŠã¿ãŸãã
tests/test_calc.py
from sample.calc import Calc def test_add(): calc = Calc() assert calc.add(1, 3) == 4 def test_minus(): calc = Calc() assert calc.minus(5, 3) == 2 def test_multiply(): calc = Calc() assert calc.multiply(2, 3) == 6 def test_divide(): calc = Calc() assert calc.divide(10, 2) == 5
pytestãã©ã®ããã«ãã¹ãã³ãŒããæ€åºãããã¯ããã¡ãã«èšèŒããããŸãã
Conventions for Python test discovery
ãtest*.pyããŸãã¯ã*test.pyããã¡ã€ã«ãæ¢çŽ¢ããé¢æ°åã«ãtestããæ¥é èŸãšããŠä»äžãããé¢æ°ããã³ã¯ã©ã¹åã«ãTestãã
æ¥é èŸãšããŠä»äžããããã¹ãé¢æ°ã察象ãšããããã§ãã
èŠããã«ããtestãïŒã¯ã©ã¹ã®å Žåã¯ãTestãïŒãä»ããŠãã ããããšã
ãŸãããã¹ãã³ãŒããèªèããŠãããã«ã¯ãinit.pyããå¿ èŠãªãããªã®ã§
Choosing a test layout / import rules
You can use Python3 namespace packages (PEP420) for your application but pytest will still perform test package name discovery based on the presence of init.py files.
äœæããŠãããŸãã
$ touch tests/__init__.py
ã§ã¯ãpytestãå®è¡ããŠã¿ãŸãã
ãŸãã¯pytestã³ãã³ããåŒæ°ãªãã§å®è¡ã
$ pytest ========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc.py .... [100%] =========================================================================== 4 passed in 0.01s ============================================================================
ãã¹ãã³ãŒããæ¢ããŠããŠãå®è¡ããŠãããŸããã
ãã¹ãã³ãŒããæå®ã
$ pytest tests/test_calc.py ========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc.py .... [100%] =========================================================================== 4 passed in 0.01s ============================================================================
ã-vããªãã·ã§ã³ãä»ããŠå®è¡ãããšããã£ãšè©³çŽ°ã«æ å ±ãåºåããŠãããŸãã
$ pytest -v ========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /path/to/venv/bin/python3 cachedir: .pytest_cache rootdir: /path/to/pytest collected 4 items tests/test_calc.py::test_add PASSED [ 25%] tests/test_calc.py::test_minus PASSED [ 50%] tests/test_calc.py::test_multiply PASSED [ 75%] tests/test_calc.py::test_divide PASSED [100%] =========================================================================== 4 passed in 0.01s ============================================================================
ã¡ãã£ãšããã¹ãã1床倱æãããŠã¿ãŸãããã
ãã¹ãã³ãŒãããã®ããã«å€æŽããŠ
def test_add(): calc = Calc() assert calc.add(1, 2) == 4 # assert calc.add(1, 3) == 4
å®è¡ã
$ pytest =========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc.py F... [100%] ================================================================================ FAILURES ================================================================================= ________________________________________________________________________________ test_add _________________________________________________________________________________ def test_add(): calc = Calc() > assert calc.add(1, 2) == 4 E assert 3 == 4 E + where 3 = <bound method Calc.add of <sample.calc.Calc object at 0x7febb711b128>>(1, 2) E + where <bound method Calc.add of <sample.calc.Calc object at 0x7febb711b128>> = <sample.calc.Calc object at 0x7febb711b128>.add tests/test_calc.py:5: AssertionError ======================================================================= 1 failed, 3 passed in 0.03s =======================================================================
ã©ãããç¶æ ã§å€±æããããããããã§ããã
> assert calc.add(1, 2) == 4 E assert 3 == 4 E + where 3 = <bound method Calc.add of <sample.calc.Calc object at 0x7febb711b128>>(1, 2) E + where <bound method Calc.add of <sample.calc.Calc object at 0x7febb711b128>> = <sample.calc.Calc object at 0x7febb711b128>.add
The writing and reporting of assertions in tests — pytest documentation
äŸå€ãã¢ãµãŒã·ã§ã³ãããå Žåã¯ããã¡ãã
Assertions about expected exceptions
確èªã§ããã®ã§ããã¹ãã³ãŒãã¯å€±æããªãããã«æ»ããŠãããŸãã
ååŠçã»åŸåŠçãå ¥ãã
ããããããã¹ãã®å®è¡ååŸã«ååŠçã»åŸåŠçãå ¥ããŠã¿ãããšæããŸãã
Fixtureã䜿ãã¿ããã§ãã
Fixture finalization / executing teardown code
å ã»ã©äœã£ããã¹ãã³ãŒããããŒã¹ã«ããŠ
$ cp tests/test_calc.py tests/test_calc_seup_teardown.py
ãããªæãã«äœæã
tests/test_calc_seup_teardown.py
from sample.calc import Calc import pytest @pytest.fixture def setup_and_teardown(): print("Setup.") yield print("Teardown.") def test_add(setup_and_teardown): calc = Calc() print("call add") assert calc.add(1, 3) == 4 def test_minus(setup_and_teardown): calc = Calc() print("call minus") assert calc.minus(5, 3) == 2 def test_multiply(): calc = Calc() print("call multiply") assert calc.multiply(2, 3) == 6 def test_divide(): calc = Calc() print("call divide") assert calc.divide(10, 2) == 5
@pytest.fixtureã§ãã³ã¬ãŒãããé¢æ°ãå®çŸ©ããŠ
@pytest.fixture def setup_and_teardown(): print("Setup.") yield print("Teardown.")
ååŸåŠçãæã¿èŸŒã¿ãããã¹ãé¢æ°ã®åŒæ°ã«èšå®ããŸãã
def test_add(setup_and_teardown): calc = Calc() print("call add") assert calc.add(1, 3) == 4 def test_minus(setup_and_teardown): calc = Calc() print("call minus") assert calc.minus(5, 3) == 2
確èªã
$ pytest tests/test_calc_seup_teardown.py =========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc_seup_teardown.py .... [100%] ============================================================================ 4 passed in 0.01s ============================================================================
ã©ããªã£ããããŸã£ããããããŸããã
ããã¯ãä»åprintã§ãããã°çã«ç¢ºèªããããšããŠããã®ã§ãããstdinïŒstdoutãpytestã§ãã£ããã£ãããŠããããã§ãã
Capturing of the stdout/stderr output — pytest documentation
ä»åã®ç¢ºèªç®çã§ã¯ããã£ããã£ããªãã«ããã°printã®æ§åã芳ãããšãã§ããŸãã
$ pytest --capture=no tests/test_calc_seup_teardown.py ## ããã㯠$ pytest -s tests/test_calc_seup_teardown.py =========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc_seup_teardown.py Setup. call add .Teardown. Setup. call minus .Teardown. call multiply .call divide . ============================================================================ 4 passed in 0.01s ============================================================================
ãã¹ãé¢æ°ã®åŒæ°ã«Fixtureãèšå®ãããaddãminusã«é¢ããŠã¯SetupãšTeardownã®ã¡ãã»ãŒãžãèŠããŠããŸããã
ã¡ãªã¿ã«ããã£ããã£ããªãã«ããªããŠããã¹ãã«å€±æããå Žåã¯
def test_add(setup_and_teardown): calc = Calc() print("call add") assert calc.add(1, 4) == 4
ãã®é¢æ°ã§ãã£ããã£ãããæ å ±ãªã©ãåºåãããŸãã
$ pytest tests/test_calc_seup_teardown.py =========================================================================== test session starts =========================================================================== platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /path/to/pytest collected 4 items tests/test_calc_seup_teardown.py F... [100%] ================================================================================ FAILURES ================================================================================= ________________________________________________________________________________ test_add _________________________________________________________________________________ setup_and_teardown = None def test_add(setup_and_teardown): calc = Calc() print("call add") > assert calc.add(1, 4) == 4 E assert 5 == 4 E + where 5 = <bound method Calc.add of <sample.calc.Calc object at 0x7fddc8d5fcc0>>(1, 4) E + where <bound method Calc.add of <sample.calc.Calc object at 0x7fddc8d5fcc0>> = <sample.calc.Calc object at 0x7fddc8d5fcc0>.add tests/test_calc_seup_teardown.py:14: AssertionError -------------------------------------------------------------------------- Captured stdout setup -------------------------------------------------------------------------- Setup. -------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------- call add ------------------------------------------------------------------------ Captured stdout teardown ------------------------------------------------------------------------- Teardown. ======================================================================= 1 failed, 3 passed in 0.03s =======================================================================
ãŸããFixtureã䜿ãããšã§ãã¹ãé¢æ°ã®åŒæ°ãèšå®ãããããã¹ãéã§ããŒã¿ãå ±æã§ãããããŸãã
Fixtures as Function arguments
ãã®ãããããèŠããŠãããŸãããã
ãŸãšã
Pythonã®ãã¹ãã£ã³ã°ãã¬ãŒã ã¯ãŒã¯ãpytestãè©ŠããŠã¿ãŸããã
ãšãããããåæ©çãªããšã¯å°ãã¯æŒãããããã®ã§ã¯ãªãã§ãããããããããã䜿ã£ãŠãã£ãŠã¿ãŸãããã