å人ãã¡ãç§ãã¡ã¯ããªãã«ãšã£ãŠçŽ æŽããããã¥ãŒã¹ãæã£ãŠããŸãã 第äžã«ãéãã§å€ªéœãã€ãã«èŒããŠããŸããããã¯ãæ¥ããã®æš©å©ãå®å
šã«åŒãç¶ãå§ããŠããããšãæå³ããŸãã 2çªç®ã®ãã¥ãŒã¹ã¯ããå°éçã§ãã3æ20æ¥ãæåã®ã¬ãã¹ã³ã¯ã³ãŒã¹
ãPython Developerãã®æ°ããã¹ã¬ããããå§ãŸããŸããããã«é¢é£ããŠãèšäºãIntroduction to Testing in Pythonãã®æåŸã®éšåãå
¬éã
ãŸã ã
è€æ°ã®ç°å¢ã§ã®ãã¹ããããŸã§ãç¹å®ã®äŸåé¢ä¿ã»ãããæã€ä»®æ³ç°å¢ã䜿çšããŠãPythonã®1ã€ã®ããŒãžã§ã³ããã¹ãããŠããŸããã ããããPythonã®ããã€ãã®ããŒãžã§ã³ãŸãã¯ããã±ãŒãžã®ããã€ãã®ããŒãžã§ã³ã§ã¢ããªã±ãŒã·ã§ã³ããã¹ãããå¿
èŠãåžžã«ãããããããŸããã Toxã¯ãè€æ°ã®ç°å¢ã§ã®ãã¹ããèªååããã¢ããªã±ãŒã·ã§ã³ã§ãã
Toxã®ã€ã³ã¹ããŒã«Toxã¯pipçµç±ã§ã€ã³ã¹ããŒã«ããããã±ãŒãžãšããŠPââyPlã§å©çšå¯èœã§ãïŒ
$ pip install tox
ã€ã³ã¹ããŒã«åŸãToxã®æ§æã«é²ãããšãã§ããŸãã
äŸåé¢ä¿ã«åãããŠToxãã«ã¹ã¿ãã€ãºããToxã¯ããããžã§ã¯ããã£ã¬ã¯ããªã®æ§æãã¡ã€ã«ã䜿çšããŠæ§æãããŸãã æ¬¡ã®ãã®ãå«ãŸããŸãã
- ãã¹ããå®è¡ããããã«å®è¡ããã³ãã³ãã
- å®è¡ã«å¿
èŠãªè¿œå ããã±ãŒãžã
- ãã¹ãçšã«éžæãããPythonã®ã¿ãŒã²ããããŒãžã§ã³ã
Toxãæ§æããããã®æ§æãåŠã¶ä»£ããã«ãã¯ã€ãã¯ã¹ã¿ãŒãã¢ããªã±ãŒã·ã§ã³ãèµ·åããããšããå§ããããšãã§ããŸãã
$ tox-quickstart
Toxæ§æããŒã«ã¯è³ªåãããŠã
tox.ini
次ã®ãããªãã¡ã€ã«ãäœæããŸãã
[tox] envlist = py27, py36 [testenv] deps = commands = python -m unittest discover
Toxãèµ·åããåã«ãããã±ãŒãžãã€ã³ã¹ããŒã«ããæé ãå«ã
setup.py
ãã¢ããªã±ãŒã·ã§ã³ãã©ã«ããŒã«ããããšã確èªããŠãã ããã ããã§ãªãå Žåã¯ã
setup.py
äœæ
ã¬ã€ãã䜿çšã
ãŠãã ãã ã
ãŸãããããžã§ã¯ããPyPlã§ã®é
åžãç®çãšããŠããªãå Žåã
tox
ããããŒã®äžã®
tox
ãã¡ã€ã«ã«æ¬¡ã®è¡ã远å ããããšã§ããã®èŠä»¶ãã¹ãããã§ããŸãã
[tox] envlist = py27, py36 skipsdist=True
setup.pyãäœæãããã¢ããªã±ãŒã·ã§ã³ãPyPlã«äŸåããŠããå Žåã
testenv
ã»ã¯ã·ã§ã³ã§ããããæç¢ºã«ããå¿
èŠããããŸãã ããšãã°ãDjangoã«ã¯æ¬¡ã®ãã®ãå¿
èŠã§ãã
[testenv] deps = django
ãã®æé ã®æåŸã«ããã¹ããå®è¡ã§ããŸãã
ããã§Toxãå®è¡ã§ããPython 2.7çšãšPython 3.6çšã®2ã€ã®ä»®æ³ç°å¢ãäœæãããŸãã Toxãã£ã¬ã¯ããªã¯
.tox/
ãšåŒã°ããŸãã ãã®äžã§ãToxã¯åä»®æ³ç°å¢ã«å¯ŸããŠ
-m unittest discover
ãå®è¡ããŸãã
ãã®ããã»ã¹ã¯ãã³ãã³ãã©ã€ã³ããToxãåŒã³åºãããšã§éå§ã§ããŸãã
$ tox
Toxã¯ãåç°å¢ã®ãã¹ãçµæãçæããŸãã ToxãåããŠèµ·åãããšãä»®æ³ç°å¢ã®äœæã«æéãããããŸãããToxã2åç®ã«å®è¡ãããšããã¹ãŠãã¯ããã«é«éã«åäœããŸãã
Toxã®çµæã¯éåžžã«åçŽã§ãã ããŒãžã§ã³ããšã«ç°å¢ãäœæãããäŸåé¢ä¿ãã€ã³ã¹ããŒã«ãããŠããããã¹ãã³ãã³ããå®è¡ãããŸãã
èŠããŠãã䟡å€ã®ãã远å ã®ã³ãã³ãã©ã€ã³ãªãã·ã§ã³ãããã€ããããŸãã
Python 3.6ãªã©ã®åäžã®ç°å¢ãå®è¡ããŸãã
$ tox -e py36
äŸåé¢ä¿ã®å€æŽãŸãã¯
ãµã€ãããã±ãŒãžãç Žæããå Žåã®ä»®æ³ç°å¢ã®åäœæïŒ
$ tox -r
ããŸã詳现ã§ã¯ãªãçµæã§Toxãå®è¡ããïŒ
$ tox -q
ãã詳现ãªåºåã§Toxãå®è¡ããïŒ
$ tox -v
Toxã®è©³çްã«ã€ããŠã¯ã
Toxããã¥ã¡ã³ããµã€ããã芧ãã ããã
ãã¹ãèªååãããŸã§ãã³ãã³ããå®è¡ããŠãã¹ããæåã§å®è¡ããŸããã ãã ãã倿Žãè¡ããããšãã«ãã¹ããèªåçã«å®è¡ããããŒãžã§ã³ç®¡çã·ã¹ãã ïŒGitãªã©ïŒã䜿çšããŠãªããžããªã«ã³ãããããããã®ããŒã«ããããŸãã ãã¹ãèªååããŒã«ã¯ãå€ãã®å ŽåãCI / CDããŒã«ãšåŒã°ãããç¶ç¶ççµ±å/ç¶ç¶çå±éããæå³ããŸãã ãã¹ããå®è¡ããã¢ããªã±ãŒã·ã§ã³ãã³ã³ãã€ã«ããŠå
¬éããããã«éçšç°å¢ã«å±éããããšãã§ããŸãã
Travis CIã¯ãå©çšå¯èœãªå€ãã®CIãµãŒãã¹ã®1ã€ã§ãã
Travis CIã¯PythonãšããŸãæ©èœããã¯ã©ãŠãã§äœæããããã¹ãŠã®ãã¹ãã®å®è¡ãèªååã§ããããã«ãªããŸããïŒ Travis CIã¯ãGitHubããã³GitLabã®ãªãŒãã³ãœãŒã¹ãããžã§ã¯ãã§ã¯ç¡æã§ããã©ã€ããŒããããžã§ã¯ãã§ã¯ææã§å©çšã§ããŸãã
éå§ããã«ã¯ããã°ã€ã³ããŠãGitHubãŸãã¯GitLabã®è³æ Œæ
å ±ã䜿çšããŠèªèšŒããŸãã æ¬¡ã«ã次ã®å
容ã®
.travis.yml
ãšãããã¡ã€ã«ãäœæããŸãã
language: python python: - "2.7" - "3.7" install: - pip install -r requirements.txt script: - python -m unittest discover
ãã®æ§æã«ãããTravis CIã«æ¬¡ã®æç€ºãäžããããŸãã
- Python 2.7ããã³3.7ã®ãã¹ãïŒãªãã·ã§ã³ã§ãããããä»ã®ãã®ã«çœ®ãæããããšãã§ããŸããïŒ
- requirements.txtã«ãªã¹ããããŠãããã¹ãŠã®ããã±ãŒãžã®ã€ã³ã¹ããŒã«ïŒäŸåé¢ä¿ããªãå Žåã¯ããã®ã»ã¯ã·ã§ã³ãåé€ã§ããŸããïŒ
- python -m unittest discoverãå®è¡ããŠãã¹ããå®è¡ããŸãã
ãã®ãã¡ã€ã«ãã³ãããããŠããã·ã¥ãããšãTravis CIã¯ããªã¢ãŒãGitãªããžããªã«ããã·ã¥ãããã³ã«ãããã®ã³ãã³ããå®è¡ããŸãã çµæã¯ãŠã§ããµã€ãã§èŠãããšãã§ããŸãã
次ã¯äœã§ããããã§ããã¹ãã®äœæããããžã§ã¯ããžã®è¿œå ãå®è¡ãããã«ã¯èªåå®è¡ã®æ¹æ³ãããã£ãã®ã§ããã¹ãã©ã€ãã©ãªã®æé·ã«å¿ããŠåœ¹ç«ã€é«åºŠãªã¡ãœããã«æ
£ããããšãã§ããŸãã
ãªã³ã¿ãŒã¢ããªã±ãŒã·ã§ã³ã®æŠèŠToxãšTravis CIã«ã¯ãã¹ãããŒã ãã»ããã¢ãããããŠããŸãã ãã®ãã¥ãŒããªã¢ã«ã§ã¯ãpython -m unittest discoverããã¹ãããŒã ãšããŠäœ¿çšããŸããã
ãããã®ããŒã«ã§1ã€ä»¥äžã®ã³ãã³ããæäŸã§ããŸããããã«ãããæ°ããããŒã«ã远å ããŠãã¢ããªã±ãŒã·ã§ã³ã®å質ãåäžãããããšãã§ããŸãã
ãã®ãããªã¢ããªã±ãŒã·ã§ã³ã®1ã€ããªã³ã¿ãŒã§ãã 圌ã¯ããªãã®ã³ãŒããèŠãŠã³ã¡ã³ããæ®ããŸãã ãããã£ãŠããšã©ãŒã«é¢ããã¢ããã€ã¹ãæäŸããæ«å°Ÿã®ã¹ããŒã¹ãä¿®æ£ããæœåšçãªãã°ãäºæž¬ããããšããã§ããŸãã
ãªã³ã¿ãŒã®è©³çްã«ã€ããŠ
ã¯ãPython Code Quality Tutorialãã芧
ãã ãã ã
flake8ã«ããããã·ããªã³ãã£ã³ã°flake8ã¯äººæ°ã®ãããªã³ã¿ãŒã§ã
PEP 8仿§ã«åŸã£ãŠã³ãŒãã®ã¹ã¿ã€ã«ã«é¢ããã³ã¡ã³ããæ®ããŸãã
pipã䜿çšããŠ
flake8
ãã€ã³ã¹ããŒã«ã§ããŸãã
$ pip install flake8
次ã«ãåäžã®ãã¡ã€ã«ããã©ã«ããŒããŸãã¯ãã³ãã¬ãŒãã«å¯ŸããŠ
flake8
ãå®è¡ã§ããŸãã
$ flake8 test.py test.py:6:1: E302 expected 2 blank lines, found 1 test.py:23:1: E305 expected 2 blank lines after class or function definition, found 1 test.py:24:20: W292 no newline at end of file
flake8
ã«ãã£ãŠ
flake8
ãããã³ãŒãå
ã®ãšã©ãŒãšèŠåã®ãªã¹ãã衚瀺ãããŸãã
flake8
ã¯ãã³ãã³ãã©ã€ã³ãŸãã¯ãããžã§ã¯ãæ§æãã¡ã€ã«ã§æ§æã§ããŸãã äžèšã®E305ãªã©ãäžéšã®ã«ãŒã«ãç¡èŠããå Žåã¯ãæ§æã§ãããèšå®ã§ããŸãã
flake8
ã¯ããããžã§ã¯ããã©ã«ããŒå
ã®
setup.cfg
ãã¡ã€ã«ãŸãã¯
flake8
ãã¡ã€ã«ã確èªããŸãã Toxã䜿çšããå Žåã¯ã
flake8
æ§æ
flake8
ã
tox.ini
远å ã§ããŸãã
ãã®äŸã§ã¯ã
.gitããã³__pycache__ãã£ã¬ã¯ããªãšE305ã«ãŒã«ãç¡èŠããŸãã ããã«ãæååã®æå€§é·ã80æåãã90æåã«å¢å ããŸãã1è¡ããã79æåãšããæšæºã®å¶éã¯ãé·ãã¡ãœããåããã¹ãå€ãå«ãæååãªãã©ã«ãä»ã®é·ãããŒã¿ã éåžžããã¹ãã§ã¯ãæååã®é·ãã120æåã«å¢ãããŸãã
[flake8] ignore = E305 exclude = .git,__pycache__ max-line-length = 90
ãŸãã¯ãã³ãã³ãã©ã€ã³ã§æ¬¡ã®ãªãã·ã§ã³ãæäŸã§ããŸãã
$ flake8 --ignore E305 --exclude .git,__pycache__ --max-line-length=90
èšå®ã®å®å
šãªãªã¹ãã¯ã
ããã¥ã¡ã³ããµã€ãã«ãããŸãã
ããã§ãCIèšå®ã«
flake8
ã远å ã§ããŸãã Travis CIã®å Žåãæ¬¡ã®ããã«ãªããŸãã
matrix: include: - python: "2.7" script: "flake8"
Travisã¯
.flake8
ã®æ§æãèªã¿åããlintãšã©ãŒãããå Žåã¯ãã«ããå®äºã§ããŸããã å¿
ã
flake8
äŸåé¢ä¿ã
requirements.txt
远å ããŠãã ããã
ã³ãŒããã©ãŒããã¿ãŒã䜿çšããç©æ¥µçãªãªã³ãã£ã³ã°flake8
ã¯ç·šéã®ã¿ãæšå¥šããããã·ããªã³ã¿ãŒã§ãããã³ãŒãã«èªåã§å
¥åããå¿
èŠããããŸãã ã³ãŒããã©ãŒããã¿ã¯ãããç©æ¥µçãªã¢ãããŒãã§ãã ã¹ã¿ã€ã«ãšã¬ã€ã¢ãŠãã«åŸã£ãŠã³ãŒããèªåçã«å€æŽããŸãã
black
ã¯éåžžã«å®¹èµŠãªããã©ãŒããã¿ãŒã§ãã èšå®ã¯ãªããéåžžã«çްå¿ã®æ³šæãæã£ãŠããŸãã ãã¹ããã€ãã©ã€ã³ã«æ¿å
¥ããã®ã«æé©ãªããŒã«ã§ãã
泚æïŒé»ã«ã¯PythonããŒãžã§ã³3.6以éãå¿
èŠã§ãã
pipã䜿çšããŠ
black
ãã€ã³ã¹ããŒã«ã§ããŸãïŒ
$ pip install black
次ã«ãã³ãã³ãã©ã€ã³ããéå§ããã«ã¯ããã©ãŒããããããã¡ã€ã«ãŸãã¯ãã£ã¬ã¯ããªãæå®ããŸãã
$ black test.py
ãã¹ãã³ãŒããã¯ãªãŒã³ã«ä¿ã€ãã¹ããäœæãããšãã¯ãéåžžã®ã¢ããªã±ãŒã·ã§ã³ãäœæãããšããããã³ãŒããã©ã°ã¡ã³ããé »ç¹ã«ã³ããŒã¢ã³ãããŒã¹ãããããšã«æ°ä»ããããããŸããã ãšãã©ãããã¹ãã¯éåžžã«å調ã«ãªããŸãããããã¯ã³ãŒãããããã§æçåãã圢ã§èœãšãçç±ã§ã¯ãããŸããã
æéãçµã€ã«ã€ããŠããã¹ãã³ãŒãã«
æè¡çãªè² åµãèç©ããæ§é ã«ããã¢ããªã±ãŒã·ã§ã³ã³ãŒãã®å€§å¹
ãªå€æŽã«å¿
èŠãªãã¹ãã倿Žããããšãéåžžã«å°é£ã«ãªãããšã倿ããŸãã
ãã¹ããäœæãããšãã¯ãDRYååã«åŸã£ãŠãã ããïŒèªåèªèº«ãç¹°ãè¿ããªãã§ãã ããã
ãã¹ããã£ã¯ã¹ãã£ãšé¢æ°ã¯ãä¿å®ã容æãªã³ãŒããèšè¿°ããããã®åªããæ¹æ³ã§ãã ãŸããèªã¿ããããå¿ããªãã§ãã ããã
flake8
ãªã©ã®
flake8
ããŒã«ããã¹ãã³ãŒãã«å±éããããšãæ€èšããŠãã ããã
$ flake8 --max-line-length=120 tests/
ç·šééã®ããã©ãŒãã³ã¹äœäžãç¹å®ããããã®ãã¹ãPythonã§ã³ãŒãããã³ãããŒã¯ããã«ã¯å€ãã®æ¹æ³ããããŸãã æšæºã©ã€ãã©ãªã«ã¯ãæ©èœãæ°åã¹ã±ãžã¥ãŒã«ããé
åžã衚瀺ããtimeitã¢ãžã¥ãŒã«ããããŸãã ãã®äŸã§ã¯ãtestïŒïŒã100åå®è¡ãããæ¬¡ã«printïŒïŒã䜿çšããŠåºåãè¡ãããŸãã
def test():
ãã¹ãã©ã³ããŒãšããŠpytestã䜿çšããå Žåã¯ãpytest-benchmarkãã©ã°ã€ã³ã確èªããŠãã ããã ãã³ãããŒã¯ãšåŒã°ããpytestãã£ã¯ã¹ãã£ãæäŸããŸãã åŒã³åºããããªããžã§ã¯ãã¯ãã¹ãŠãã³ãããŒã¯ïŒïŒã«æž¡ãããšãã§ããåŒã³åºãããpytestã®çµæã®æéãè§£æããŸãã
pipã䜿çšããŠPyPlããpytest-benchmarkãã€ã³ã¹ããŒã«ã§ããŸãã
$ pip install pytest-benchmark
次ã«ããã£ã¯ã¹ãã£ã䜿çšããŠãåŒã³åºããããªããžã§ã¯ããå®è¡ã«æž¡ããã¹ãã远å ã§ããŸãã
def test_my_function(benchmark): result = benchmark(test)
pytestãå®è¡ãããšããã³ãããŒã¯çµæãåŸãããŸãã

詳现ã«ã€ããŠã¯ã
ããã¥ã¡ã³ããµã€ããã芧ãã ããã
ã»ãã¥ãªãã£ãšã©ãŒãç¹å®ããããã®ãã¹ãã¢ããªã±ãŒã·ã§ã³ã§å®è¡ããå¿
èŠããããã1ã€ã®ãã¹ãã¯ãäžè¬çãªãšã©ãŒãšã»ãã¥ãªãã£ã®è匱æ§ããã§ãã¯ããããšã§ãã
pipã䜿çšããŠPyPlããBanditãã€ã³ã¹ããŒã«ããŸãã
$ pip install bandit
次ã«ã
-r
ãã©ã°ã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ã¢ãžã¥ãŒã«ã®ååãæž¡ããç°¡åãªæ
å ±ãååŸã§ããŸãã
$ bandit -r my_sum [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.5.2 Run started:2018-10-08 00:35:02.669550 Test results: No issues identified. Code scanned: Total lines of code: 5 Total lines skipped (
flake8
ãš
flake8
ã«ã
bandit
ãã©ã°
bandit
ãèšå®ã§ããŸãããããã®äžéšãç¡èŠããå Žåã¯ããã©ã¡ãŒã¿ãŒã䜿çšããŠ
setup.cfg
ãã¡ã€ã«ã«æ¬¡ã®ãã©ã°ã¡ã³ãã远å ã§ããŸãã
[bandit] exclude: /test tests: B101,B102,B301
GitHub Webãµã€ãã®è©³çްã
ãããã«Pythonã¯ãã¢ããªã±ãŒã·ã§ã³ã®æ£ããåäœã確èªããããã«å¿
èŠãªçµã¿èŸŒã¿ã³ãã³ããšã©ã€ãã©ãªã®ãããã§ããã¹ããå©çšå¯èœã«ããŸããã Pythonã§ãã¹ããéå§ããã®ã¯ç°¡åã§ããunittestã䜿çšããŠãå°ãããŠä¿å®ããããã¡ãœãããèšè¿°ããŠãã³ãŒãããã¹ãã§ããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ããšæ¡åŒµã®è©³çްã«ã€ããŠã¯ãpytestãªã©ã®ãã¹ããã¬ãŒã ã¯ãŒã¯ã®ããããã«ã¢ããã°ã¬ãŒãããŠãããé«åºŠãªæ©èœã®äœ¿çšãéå§ããããšãæ€èšããŠãã ããã
èªãã§ãããŠããããšãã Pythonã§çŽãããªãæªæ¥ãïŒ
ãããŠãèšäºãèªãã 人ã®ããã«ãç§ãã¡ã¯å¥ã®çŽ æŽããããã¥ãŒã¹ãæã£ãŠããŸãã ä»ã10,000ã«ãŒãã«ã®å²åŒã§
Pythonéçºè
ã³ãŒã¹ãååŸããããšãã§ããŸãïŒ
å線第äºéš