آزمون‌نویسی در مسیر Python

بیاموزید چطور تمرین‌های Python خود را در Exercism آزمون کنید.


ما از pytest به‌عنوان اجراکننده‌ی تست وب‌سایت خود استفاده می‌کنیم. اگر می‌خواهید تست‌های تمرین‌های مسیر Python را به‌صورت محلی دانلود و اجرا کنید، باید pytest را روی دستگاه توسعه‌ی خود نصب کنید. همچنین بهتر است افزونه‌های زیر را برای pytest نصب کنید:

ما استفاده از برنامه‌ی بررسی کد pylint را هم توصیه می‌کنیم، چون بخشی از بازخورد خودکار ما در وب‌سایت است و می‌تواند ابزار بسیار مفیدی برای تحلیل ایستای کد باشد. برای سهولت استفاده، افزونه‌ی pytest-pylint برای pytest به شما اجازه می‌دهد pylint را از طریق pytest در خط فرمان اجرا کنید.

پیکربندی Pylint می‌تواند کمی دشوار باشد، بنابراین این آموزش از pylint.readthedocs.io می‌تواند برای شروع مفید باشد، و همین‌طور این مرور Code Quality: Tools and Best Practices از Real Python.

نصب pytest

Pytest را می‌توان با استفاده از ابزار توکار Python، یعنی pip، نصب و به‌روزرسانی کرد.

برای نکات بیشتر، Brett Cannon یک راهنمای سریع و کاربردی برای نصب بسته‌ها در python نوشته است، همراه با توضیحی عالی درباره‌ی اینکه چرا باید از python -m pip استفاده کنید. برای اطلاعات بیشتر درباره‌ی آرگومان‌های خط فرمان Python، بخش خط فرمان و محیط را در مستندات Python ببینید.

توجه: ممکن است Python3 و py روی سیستم شما نام‌های مستعار Python باشند یا نباشند. لطفاً دستورهای نصب زیر را بر همین اساس تنظیم کنید. برای نصب pytest در یک محیط مجازی، پیش از اجرای دستورها مطمئن شوید که محیط فعال شده است. در غیر این صورت، نصب pytest به‌صورت سراسری انجام می‌شود.

Windows

PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

Linux / MacOS

$ python3 -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

برای بررسی موفقیت‌آمیز بودن نصب:

$ python3 -m pytest --version
pytest 8.3.3

اجرای تست‌ها

برای اجرای تست‌ها، با استفاده از cd در ترمینال خود به پوشه‌ای بروید که تمرین در آن ذخیره شده است (در ادامه <exercise-folder-location> را با مسیر خود جایگزین کنید).

$ cd <exercise-folder-location>

Note

<exercise-folder-location> یا بیشتر مواردی که داخل پرانتزهای زاویه‌دار می‌آیند، نشان‌دهنده‌ی یک مقدار جای‌نگهدار هستند. یک مسیر یا نام فایل معمولی باید بدون هیچ پرانتزی نوشته شود.

برای مثال: /Users/janedoe/exercism/python/exercises/concept/chaitanas-colossal-coaster (در سیستم‌های *nix)، C:\Users\janedoe\exercism\python\exercises\practice\hello-world\ (در Windows)، myFolder یا my_file.py.


فایلی که می‌خواهید اجرا کنید معمولاً به _test.py ختم می‌شود. این فایل شامل تست‌های راه‌حل تمرین است و همان تست‌هایی است که هنگام بارگذاری راه‌حل در وب‌سایت اجرا می‌شوند. سپس دستور زیر را در ترمینال خود اجرا کنید و <exercise_test.py> را با مسیر یا نام فایل تست جایگزین کنید:

$ python3 -m pytest -o markers=task <exercise_test.py>
==================== 7 passed in 0.08s ====================

رفع هشدارها

اگر از pytest -o markers=task در دستور بالا استفاده نکنید، ممکن است هنگام اجرای تستی که از نحوه‌ی نگارش جدید ما استفاده می‌کند، warningsهایی درباره‌ی «unknown markers» دریافت کنید.

برای اینکه مجبور نباشید برای هر تستی که اجرا می‌کنید pytest -o markers=task را تایپ کنید، می‌توانید از فایل پیکربندی pytest.ini استفاده کنید که می‌توانید آن را از سطح بالای پوشه‌ی مسیر Python دانلود کنید: pytest.ini.

همچنین می‌توانید فایل pytest.ini خودتان را با محتوای زیر بسازید:

[pytest]
markers =
    task: A concept exercise task.

اگر این فایل را در پوشه‌ی ریشه یا کاری تمرین‌های Exercism قرار دهید، نشانه‌ها ثبت می‌شوند و هشدارها متوقف می‌شوند. اطلاعات بیشتر درباره‌ی نشانه‌های pytest را می‌توانید در مستندات pytest درباره‌ی نشانه‌گذاری توابع تست و مستندات pytest درباره‌ی کار با نشانه‌های سفارشی پیدا کنید.

اطلاعات بیشتر درباره‌ی سفارشی‌سازی پیکربندی‌های pytest را می‌توانید در مستندات pytest درباره‌ی قالب‌های فایل پیکربندی پیدا کنید

شکست تست‌ها

وقتی تست‌ها شکست می‌خورند، pytest متن هر تست شکست‌خورده را همراه با مقدارهای return مورد انتظار و واقعی هرکدام در ترمینال چاپ می‌کند. در زیر یک مثال کلی از یک تست شکست‌خورده آمده است:

$(my_venv) python3 -m pytest -o markers=task <exercise_test.py>

=================== FAILURES ====================
______________ name_of_failed_test ______________
# Test code inside of <exercise_test.py> that failed.
...
E   TypeOfError: ReturnedValue != ExpectedValue

exercise_test.py:<line_of_failed_test>: TypeOfError
============ short test summary info ============
FAILED exercise_test.py::ExerciseTest::name_of_failed_test
========== 1 failed, 2 passed in 0.13s ==========

آرگومان‌های اضافی

اگر می‌خواهید دقیقاً مشخص کنید که pytest چه چیزی روی صفحه‌ی شما برمی‌گرداند، این چند آرگومان کاربردی خط فرمان به شما امکان می‌دهند رفتار آن را پیکربندی کنید.

نمایش همه‌ی جزئیات [-v]

افزودن پرچم -v (verbose) علاوه بر شکست‌های تست، هم اطلاعات محیط و هم خلاصه‌ی تست را برمی‌گرداند:

$(my_venv)  python3 -m pytest -o markers=task -v exercises/<exercise_name>/<test_file_test.py>

======================================== test session starts ===========================================
platform darwin -- Python 3.9.0, pytest-6.2.5, -- /usr/local/envs/my_env/bin/python3
cachedir: .pytest_cache
metadata: {'Python': '3.9.0', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1'}, 'Plugins': {'subtests': '0.5.0', 'pylint': '0.18.0'}
rootdir: /Users/<user>/exercism/python, configfile: pytest.ini
plugins: subtests-0.5.0, pylint-0.18.0

collected 5 items

exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_one FAILED                          [ 20%]
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_two FAILED
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_three PASSED                        [ 40%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_four FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_five PASSED                 [ 60%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_six FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_seven PASSED                [ 80%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_eight FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_nine PASSED                 [100%]

================================================ FAILURES ======================================================
# Failed tests are then individually printed out below

.......

توقف پس از اولین شکست [-x]

استفاده از پرچم -x تست‌ها را به‌طور عادی اجرا می‌کند، اما اجرای تست را در اولین شکست متوقف می‌کند. این کار زمانی کمک می‌کند که بخواهید هر بار فقط یک تکلیف یا یک شکست تست را دیباگ کنید:

$(my_venv) python3 -m pytest -o markers=task -x exercises/<exercise_name>/<test_file_test.py>

=================== FAILURES ====================
_______________ example_test_foo ________________
...
...
============ short test summary info ============
FAILED example_test.py::ExampleTest::example_test_foo
!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!
========== 1 failed, 5 passed in 0.28s ==========

اول تست‌های شکست‌خورده [--ff]

افزونه‌ی pytest-cache به خاطر می‌سپارد که آخرین بار که pytest را اجرا کردید کدام تست‌ها شکست خورده بودند، بنابراین استفاده از پرچم --ff به pytest می‌گوید که تست‌های شکست‌خورده‌ی قبلی را اول اجرا کند و سپس با بقیه‌ی تست‌ها ادامه دهد. اگر اصلاحات کوچک زیادی روی یک تکلیف یا مجموعه‌ی ورودی خاص انجام می‌دهید، این کار ممکن است سرعت تست کردن شما را بالا ببرد.

$(my_venv) python3 -m pytest -o markers=task --ff <example_file_test.py>
==================== 7 passed in 503s ====================

روند کاری پیشنهادی

توصیه می‌کنیم برای اینکه دیباگ کردن را برایتان ساده‌تر و (احتمالاً) سریع‌تر کنیم، از دستورهای زیر استفاده کنید:

ابتدا پوشه‌ی کاری خود را به پوشه‌ی تمرینی که می‌خواهید تست کنید تغییر دهید:

$(my_venv) cd path/to/exercise

سپس تست‌ها را همراه با آرگومان‌های -x و --ff که پیش‌تر توضیح داده شد اجرا کنید:

$(my_venv) python3 -m pytest -o markers=task -x --ff <example_file_test.py>

این کار راه‌حل شما را تست می‌کند. وقتی pytest به یک تست شکست‌خورده برسد، برنامه متوقف می‌شود و به شما می‌گوید کدام تست شکست خورده است. وقتی اصلاحاتی انجام دهید و تست را دوباره اجرا کنید، pytest ابتدا تست قبلی را که شکست خورده بود اجرا می‌کند و سپس با بقیه‌ی تست‌ها ادامه می‌دهد.

استفاده از PDB، دیباگر Python، همراه با pytest

اگر می‌خواهید «مثل یک حرفه‌ای دیباگ کنید»، می‌توانید آرگومان --pdb را بعد از دستور pytest به کار ببرید و به دیباگر Python داخلی، یعنی PDB، وارد شوید.

$(my_venv) python3 -m pytest -o markers=task -x --ff --pdb <example_file_test.py>
=============== 4 passed in 0.15s ===============

وقتی تستی شکست می‌خورد، وارد شدن به PDB به شما اجازه می‌دهد کد خود را خط به خط اجرا کنید و محدوده‌ی جاری را ببینید، و همچنین مقدار متغیرها و امضای توابع مختلف را بررسی کنید. جزئیات بیشتر درباره‌ی ماژول PDB را می‌توانید در مستندات Python درباره‌ی PDB پیدا کنید. علاوه بر این، مستندات pytest درباره‌ی PDB و این راهنما از Real Python بسیار مفید هستند.

گسترش IDE شما

اگر می‌خواهید IDE خود را با ابزارهایی گسترش دهید که در تست کردن و بهتر کردن کدتان به شما کمک می‌کنند، صفحه‌ی ابزارها را ببینید. ما در آنجا چند IDE، ویرایشگر و چند افزونه‌ی مفید برای linting و دیباگ کردن را بررسی می‌کنیم.

اطلاعات بیشتر

افزودن python به PATH شما

توجه: اگر Python را در Windows با PSF Installer نصب کرده‌اید، دستور py خواهد بود، نه python3.

تایپ کردن python3 -m هر بار که می‌خواهید یک ماژول را اجرا کنید می‌تواند کمی آزاردهنده باشد. برای جلوگیری از این کار، می‌توانید پوشه‌ی Scripts نصب Python خود را به PATH اضافه کنید. اگر نمی‌دانید Python را کجا نصب کرده‌اید، دستور زیر را در ترمینال خود اجرا کنید:

$ python3 -c "import os, sys; print(os.path.dirname(sys.executable))"
<python_directory>

پوشه‌ی بازگردانده‌شده همان جایی است که نسخه‌ی فعال فعلی Python شما نصب شده است؛ در این بخش به آن <python_directory> می‌گوییم.

Windows

روی دکمه‌ی Windows Start کلیک کنید و عبارت Edit the system environment variables را جست‌وجو کنید و کلید Enter را فشار دهید. سپس Environment Variables... را فشار دهید:

دکمه‌ی آبی را فشار دهید، لول

سپس متغیر Path را در User variables پیدا کنید، آن را انتخاب کنید و روی Edit... کلیک کنید:

انتخاب متغیر path

سپس همان‌طور که در تصویر نشان داده شده، یک خط جدید اضافه کنید و <python_directory> را با پوشه‌ی نصب Python خود جایگزین کنید:

افزودن python به path

MacOS/Linux

موارد زیر باید برای بیشتر توزیع‌های Linux و MacOS با پوسته‌ی bash کار کنند. دستورها ممکن است بسته به توزیع Linux و اینکه از پوسته‌ی fish یا zsh استفاده می‌کنید، متفاوت باشند. <python_directory> را با خروجی python3 -c "import os, sys; print(os.path.dirname(sys.executable))" جایگزین کنید.

export PATH="$PATH:<python_directory>"