2021-02-12 15:08:36 +01:00
# Tests
2022-03-29 18:45:14 -07:00
We use [pytest][Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to download and run exercise tests for the Python track locally.
You should also install the following `pytest` plugins:
2021-11-15 18:57:10 +01:00
2022-03-29 18:45:14 -07:00
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
2021-11-15 18:57:10 +01:00
2022-03-29 18:45:14 -07:00
We also recommend using the code linting program [pylint][pylint], as it is part of our automated feedback on the website and can be a very useful static code analysis tool.
For ease-of-use, the [pytest-pylint][pytest-pylint] plugin for `pytest` will allow you to run `pylint` via `pytest` on the command line.
2021-11-15 18:57:10 +01:00
2024-07-24 13:30:42 -05:00
Pylint configuration can be a bit much, so this [tutorial from pylint.readthedocs.io][tutorial from pylint.readthedocs.io] can be helpful for getting started, as can this overview of [Code Quality: Tools and Best Practices][Code Quality: Tools and Best Practices] from Real Python.
2021-11-15 18:57:10 +01:00
2022-03-29 18:45:14 -07:00
## Installing pytest
2021-11-15 18:57:10 +01:00
2022-03-29 18:45:14 -07:00
Pytest can be installed and updated using the built-in Python utility [`pip` ][pip].
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
For additional tips, Brett Cannon has a nice [quick-and-dirty guide on how to install packages for python][quick-and-dirty], along with a great explanation on [why you should use `python -m pip` ][python-m-pip].
For more on Python's command line arguments, see [command line and environment][python command line] in the Python documentation.
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
**Note:** `Python3` and `py` may or may not be aliases for Python on your system.
Please adjust the install commands below accordingly.
To install `pytest` in a virtual environment, ensure the environment **is activated** prior to executing commands.
Otherwise, the `pytest` installation will be global.
2021-10-22 00:46:15 +02:00
#### Windows
```powershell
2022-03-29 18:45:14 -07:00
PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
2023-07-16 15:09:14 -07:00
Successfully installed pytest-7.2.2 ...
2021-10-22 00:46:15 +02:00
```
2016-01-26 16:56:50 +01:00
2021-10-22 00:46:15 +02:00
#### Linux / MacOS
2016-03-25 18:31:00 -04:00
2021-10-22 00:46:15 +02:00
```bash
$ python3 -m pip install pytest pytest-cache pytest-subtests pytest-pylint
2023-07-16 15:09:14 -07:00
Successfully installed pytest-7.2.2 ...
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
```
2021-09-01 06:56:21 -07:00
2022-03-29 18:45:14 -07:00
To check if installation was successful:
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
```bash
$ python3 -m pytest --version
2023-07-16 15:09:14 -07:00
pytest 7.2.2
2021-10-22 00:46:15 +02:00
```
2021-09-04 10:46:29 -05:00
2022-03-29 18:45:14 -07:00
## Running the tests
2021-09-01 06:56:21 -07:00
2022-03-29 18:45:14 -07:00
To run the tests, go to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
2021-09-01 06:56:21 -07:00
2022-03-29 18:45:14 -07:00
```bash
$ cd {exercise-folder-location}
```
2021-09-01 06:56:21 -07:00
2022-03-29 18:45:14 -07:00
The file you will want to run usually ends in `_test.py` .
This file contains the tests for the exercise solution, and are the same tests that run on the website when a solution is uploaded.
Next, run the following command in your terminal, replacing `{exercise_test.py}` with the location/name of the test file:
2017-10-24 17:13:34 -04:00
```bash
2022-03-29 18:45:14 -07:00
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
2017-10-24 17:13:34 -04:00
```
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
### Fixing warnings
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
If you do not use the `pytest -o markers=task` in the command above, is possible that you will get `warnings` about "unknown markers" when running a test that uses our _new_ syntax.
2017-10-24 17:13:34 -04:00
2022-03-29 18:45:14 -07:00
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file, which can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
2017-10-24 17:13:34 -04:00
2022-03-29 18:45:14 -07:00
You can also create your own `pytest.ini` file with the following content:
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
```ini
[pytest]
markers =
task: A concept exercise task.
2021-10-22 00:46:15 +02:00
```
2016-01-26 16:56:50 +01:00
2022-03-29 18:45:14 -07:00
Placing this file in the _root_ or _working_ directory for Exercism exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][marking test functions with attributes] and the `pytest` documentation on [working with custom markers][working with custom markers].
_More information on customizing pytest configurations can be found in the pytest documentation on [configuration file formats][configuration file formats]_
### Test Failures
When tests fail, `pytest` prints the text of each failed test, along with the expected and actual `return` values of each to the terminal.
Below is an generic example of a failed test:
2016-03-25 18:31:00 -04:00
```bash
2022-03-29 18:45:14 -07:00
$(my_venv) python3 -m pytest -o markers=task {exercise_test.py}
2021-10-22 00:46:15 +02:00
=================== 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 ==========
2016-01-26 16:56:50 +01:00
```
2021-10-22 00:46:15 +02:00
### Extra arguments
2022-03-29 18:45:14 -07:00
If you really want to be specific about what pytest returns on your screen, here are some handy command-line arguments that allows you to configure its behavior.
#### Return All Details [`-v`]
Adding the `-v` (_verbose_) flag will return both environment information and a test summary in addition to test failures:
```bash
2024-07-24 13:30:42 -05:00
$(my_venv) python3 -m pytest -o markers=task -v exercises/< exercise_name > /< test_file_test.py >
2022-03-29 18:45:14 -07:00
======================================== 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
2024-07-24 13:30:42 -05:00
collected 5 items
2022-03-29 18:45:14 -07:00
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
.......
```
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
#### Stop After First Failure [`-x`]
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
Using the `-x` flag will run the tests as normal, but stop the test run upon the first test failure.
This helps when you want to debug a single task or test failure at a time:
2021-10-22 00:46:15 +02:00
```bash
2024-07-24 13:30:42 -05:00
$(my_venv) python3 -m pytest -o markers=task -x exercises/< exercise_name > /< test_file_test.py >
2022-03-29 18:45:14 -07:00
2021-10-22 00:46:15 +02:00
=================== 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 ==========
2016-03-25 18:31:00 -04:00
```
2021-10-22 00:46:15 +02:00
#### Failed Tests First [`--ff`]
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
The `pytest-cache` plugin remembers which tests failed last time you ran `pytest` , so using the flag `--ff` will tell `pytest` to run previously failed tests **first** , then continue with the remainder of the tests.
This might speed up your testing if you are making a lot of smaller fixes around one particular task or set of inputs.
2021-10-22 00:46:15 +02:00
```bash
2022-03-29 18:45:14 -07:00
$(my_venv) python3 -m pytest -o markers=task --ff < example_file_test.py >
2021-10-22 00:46:15 +02:00
==================== 7 passed in 503s ====================
2016-01-26 16:56:50 +01:00
```
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
### Recommended Workflow
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
We recommend using the following commands to make your debugging easier and (possibly) faster:
2016-03-25 18:31:00 -04:00
2021-10-22 00:46:15 +02:00
First change your working directory to the directory of the exercise you want to test:
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
```bash
2022-03-29 18:45:14 -07:00
$(my_venv) cd path/to/exercise
2021-10-22 00:46:15 +02:00
```
2021-09-01 06:56:21 -07:00
2021-10-22 00:46:15 +02:00
Then, run the tests together with the previously explained arguments `-x` and`--ff` :
2016-03-25 18:31:00 -04:00
```bash
2022-03-29 18:45:14 -07:00
$(my_venv) python3 -m pytest -o markers=task -x --ff < example_file_test.py >
2016-01-26 16:56:50 +01:00
```
2022-03-29 18:45:14 -07:00
This will test your solution.
When `pytest` encounters a failed test, the program will stop and tell you which test failed.
When you make fixes and run the test again, `pytest` will first run the previous test that failed, then continue with the remaining tests.
### Using PDB, the Python Debugger, with pytest
2016-01-26 16:56:50 +01:00
2022-03-29 18:45:14 -07:00
If you want to "debug like a pro", you can use the `--pdb` argument after the `pytest` command, and drop into the built-in [Python debugger][pdb], `PDB` .
2016-03-25 18:31:00 -04:00
```bash
2022-03-29 18:45:14 -07:00
$(my_venv) python3 -m pytest -o markers=task -x --ff --pdb < example_file_test.py >
2021-10-22 00:46:15 +02:00
=============== 4 passed in 0.15s ===============
2016-01-26 16:56:50 +01:00
```
2022-03-29 18:45:14 -07:00
When a test fails, dropping into `PDB` will allow you to step through your code viewing the current scope, as well as checking the value of variables and the signature of different functions.
More details on the `PDB` module can be found in the [Python documentation on PDB][pdb].
Additionally, the [pytest docs on PDB][pytest-pdb] and [this guide from Real Python ](https://realpython.com/python-debugging-pdb/ ) are extremely helpful.
2021-10-22 00:46:15 +02:00
## Extending your IDE
2016-01-26 16:56:50 +01:00
2022-03-29 18:45:14 -07:00
If you'd like to extend your IDE with some tools that will help you with testing and improving your code, check the [tools ](./tools ) page.
We explore multiple IDEs, editors and some useful extensions for linting and debugging there.
2021-10-22 00:46:15 +02:00
## Additional information
2022-03-29 18:45:14 -07:00
### Adding python to your PATH
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
**Note:** If you have installed Python on Windows via the [PSF Installer][psf-installer], then the command will be `py` as opposed to `python3` .
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
Typing `python3 -m` every time you want to run a module can get a little annoying.
To avoid this, you can add the `Scripts` folder of your Python installation to your path.
If you do not know where you have installed Python, run the following command in your terminal:
2016-03-25 18:31:00 -04:00
```bash
2021-10-22 00:46:15 +02:00
$ python3 -c "import os, sys; print(os.path.dirname(sys.executable))"
{python_directory}
2016-03-25 18:31:00 -04:00
```
2022-03-29 18:45:14 -07:00
The _returned_ directory is where your current active Python version is installed, in this section it is referred to as `{python_directory}` .
2016-03-25 18:31:00 -04:00
2021-10-22 00:46:15 +02:00
#### Windows
2016-03-25 18:31:00 -04:00
2022-03-29 18:45:14 -07:00
Click the `Windows Start` button and lookup _Edit the system environment variables_ and press enter.
Next press, `Environment Variables...` :
2021-10-22 00:46:15 +02:00

2021-11-15 18:57:10 +01:00
Then find the `Path` variable in your _User variables_ , select it, and click `Edit...` :
2021-10-22 00:46:15 +02:00

Then add a new line, as shown in the picture, replacing `{python_directory}` with your Python installation's directory:

2022-03-29 18:45:14 -07:00
#### MacOS/Linux
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
The below should work for most Linux and MacOS flavors with a `bash` shell.
Commands may vary by Linux distro, and whether a `fish` or `zsh` shell is used.
Replace `{python_directory}` with the output of `python3 -c "import os, sys; print(os.path.dirname(sys.executable))"`
2021-10-22 00:46:15 +02:00
2022-03-29 18:45:14 -07:00
```bash
export PATH=”$PATH:{python_directory}}”
2016-03-25 18:31:00 -04:00
```
2021-10-22 00:46:15 +02:00
2022-03-29 18:50:50 -07:00
[Code Quality: Tools and Best Practices]: https://realpython.com/python-code-quality/
[Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration -file-formats
[marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising -errors-on-unknown-marks
[pdb]: https://docs.python.org/3.9/library/pdb.html
2022-03-29 18:45:14 -07:00
[pip]: https://pip.pypa.io/en/stable/getting-started/
2022-03-29 18:50:50 -07:00
[psf-installer]: https://www.python.org/downloads/
[pylint]: https://pylint.pycqa.org/en/latest/user_guide/
2024-07-24 13:30:42 -05:00
[pytest-cache]: http://pythonhosted.org/pytest-cache/
2022-03-29 18:50:50 -07:00
[pytest-pdb]: https://docs.pytest.org/en/6.2.x/usage.html#dropping -to-pdb-python-debugger-on-failures
2024-07-24 13:30:42 -05:00
[pytest-pylint]: https://github.com/carsongee/pytest-pylint
[pytest-subtests]: https://github.com/pytest-dev/pytest-subtests
2022-03-29 18:45:14 -07:00
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
2022-03-29 18:50:50 -07:00
[python command line]: https://docs.python.org/3/using/cmdline.html
2022-03-29 18:45:14 -07:00
[python-m-pip]: https://snarky.ca/why-you-should-use-python-m-pip/
2022-03-29 18:50:50 -07:00
[quick-and-dirty]: https://snarky.ca/a-quick-and-dirty-guide-on-how-to-install-packages-for-python/
2024-07-24 13:30:42 -05:00
[tutorial from pylint.readthedocs.io]: https://pylint.readthedocs.io/en/v2.17.7/tutorial.html
2022-03-29 18:45:14 -07:00
[working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working -with-custom-markers