how to make a Python Test Driven Development environment manually
This is one way to make a Python Test Driven Development project. I walk through making the folders (directories) and files for the environment, including setting up the first test.
By the end of the chapter you will know these commands better
cd
tree
mkdir
touch
cat
mv
python3 -m unittest
history
cd
tree
mkdir
New-Item
cat
Move-Item
python -m unittest
history
requirements
questions about making a Python Test Driven Development Environment
how to setup the project
I choose
personas the name of this projectI open a terminal
I change directory to where I will put all the projects from this book. I type cd in the terminal
Note
skip this step if you are already in the
pumping_pythondirectory or made it earliercd pumping_pythonif the terminal shows
cd: no such file or directory: pumping_pythonthe folder (directory) does NOT exist. I need to make it. I use the mkdir program to make the
pumping_pythonfolder (directory)mkdir pumping_pythonI try changing directory again
cd pumping_pythonthe terminal shows I am in the
pumping_pythonfolder (directory).../pumping_python
I type tree in the terminal to see what files and folders are in the
pumping_pythondirectorytreethe terminal shows
. 0 directories, 0 filesNote
If you have done other work in the
pumping_pythonfolder there will be files and folders not 0 directories and 0 filesI change directory to the
personproject in thepumping_pythonfoldercd personthe terminal is my friend, and shows
cd: no such file or directory: personthere is no folder with the name
personin this folder, time to makeperson.
how to setup a project with uv
I use the uv Python Package Manager to setup the project
uv init personthe terminal shows
Initialized project `person` at `.../pumping_python/person`uv is a program that makes files and folders needed for a project. It also handles Python and Python Packages.
how to change directory to the project
I try to change directory to the
personfolder againcd personthe terminal shows
.../pumping_python/personI use tree to see what uv added to the folder
tree -a -L 2the
-Loption tells tree how deep to go when showing the folders and files, I use2to make it show only the first level of contents of the child folders.treeuv added a few files and folders:
.gitthis folder makes the project a git repository, which makes it easy to keep track of changes I make. If I publish the repository I can work on the project from any computer anywhere (as long as it is has access to the repository)..gitignoreis a file that tells git what files in the project to not keep track of, which helps if there are things I do not want or need to share..python-versionis a file that has the version of Python I am using, it helps if I do projects with different Python versions.README.mdis a file that is used to describe the project.srcis a folder that contains the code for the project (the source code).pyproject.tomlis a file that is used to configure Python projects for packaging see pyproject.toml for more
how to see what is inside a file
I can use the cat program to see what is inside
.gitignorecat .gitignoreI use cat to see what is inside
pyproject.tomlcat pyproject.tomlI use cat to see what is in
.python-versioncat .python-versionthe terminal is my friend, and shows
3.XYwhere
XYare numbers like14depending on what version of Python is installed.I use cat to show what is inside
README.mdcat README.md
how to run a Python program
I use Python to run the
personprogrampython3 src/person/__init__.pypython src/person/__init__.pyNo errors! The uv Python package manager made the file with some Python code in it that I can run.
I can also use uv to run the program
uv run personthe terminal shows
Hello from person!Success!
I use cat to see what is in
src/person/__init__.pycat src/person/__init__.py
test_failure
how to manually run tests
I use the unittest module from The Python Standard Library that comes with Python to run tests. I type this in the terminal
python3 -m unittestpython3is the Python programpython -m unittestpythonis the Python programthe terminal shows
------------------------------------------------------ Ran 0 tests in 0.000s NO TESTS RANbecause I do not have any tests, yet.
-mis an option/switch passed when calling Python to run the module ( unittest in this case)which leads to the question of what is a module? Any file that ends in
.pyis a Python module.
how to make a directory for the tests
how to make a Python file for the tests in the ‘tests’ directory
the terminal is my friend, and shows
NO TESTS RAN
because I have not set up the test correctly.
RED: make it fail
I open
person.pyfrom thetestsfolderI add Python code to
tests/person.pyNote
the line numbers are a guide, no need to copy them
1False is TrueI turn on the
Auto Savefeature in the Integrated Development Environment (IDE) to automatically save files when I make a change so that I do not repeat myself. I do not want to use ctrl/command+s (Windows & Linux/MacOS) on the keyboard every time I make a change, I want the computer to do that for meAttention
Turn on the
Auto Savefeature in the Integrated Development Environment (IDE)I try the command again to run the tests, in the terminal
python3 -m unittestpython -m unittestthe terminal is my friend, and shows
NO TESTS RANbecause the
testsfolder is NOT a Python package and unittest cannot find my test.I need to add a file named
__init__.pyto thetestsfolder, to make it a Python package for unittest to find the test.
how to make the tests a Python package
Danger
use 2 underscores (__) before and after init for __init__.py not _init_.py
the terminal does not feel like my friend, and shows
NO TESTS RAN
because unittest does not know that tests/person.py is a test file.
I have to change the name to make sure it starts with test_.
how to change the name of a file
I close
tests/person.pyDanger
if you do not close
tests/person.py, there will be 3 files in thetestsfolder after the next step (instead of 2), because theAuto Savefeature (enabled earlier) will save the original file if it is still open after you change its name.
I use the mv program to change the name of
tests/person.pytotests/test_person.pymv tests/person.py tests/test_person.pyI use tree with the
-Loption to see what I have so fartree -a -L 2I run the test again
python3 -m unittest
the terminal still shows NO TESTS RAN
I add assert before
False is Trueintests/test_person.py1# False is True 2assert False is TrueI try to run the test again
python3 -m unittestpython -m unittestthe terminal is my friend, and shows AssertionError
E ====================================================== ERROR: tests.test_person (unittest.loader._FailedTest.tests.test_person) ------------------------------------------------------ ImportError: Failed to import test module: tests.test_person Traceback (most recent call last): File "/usr/local/lib/python3.XY/unittest/loader.py", line ABC, in _find_test_path module = self._get_module_from_name(name) File "/usr/local/lib/python3.XY/unittest/loader.py", line DEF, in _get_module_from_name __import__(name) ~~~~~~~~~~^^^^^^ File ".../pumping_python/person/tests/test_person.py", line 2, in <module> assert False is True ^^^^^^^^^^^^^ AssertionError ------------------------------------------------------ Ran 1 test in 0.001s FAILED (errors=1)Success! I have my first failure.
I can use any name for the test file as long as it starts with
testor unittest will NOT run the tests in the file.This is the RED part of the Test Driven Development Cycle. The message in the terminal is about the failure, I like to read these from the bottom up. Here is an explanation of each line, starting from the last line on the screen
FAILED (errors=1): the number of failures or errorsRan 1 test in A.XYZs: the number of tests it ran and how long they tookAssertionError: the Error (Exception) that happened. Since I used an assert statement I get AssertionError because the statement afterassertis False - False is NOT Trueassert False is True: the line of code that caused AssertionErrorthe arrows (
^^^^^^^^^^^^^): point to the part of the line above, that Python thinks caused the errorFile ".../pumping_python/person/tests/test_person.py", line 2, in <module>: the line number of the code that caused the error and the location of the file where the error happened and again the question - what is a module?__import__(name)shows another error that is triggered by the one fromassert False is TrueFile "/usr/local/lib/python3.XY/unittest/loader.py", line 367, in _get_module_from_nameshows the line, method and file where the error triggered by myassert False is Truehappenedmodule = self._get_module_from_name(name)a failure triggered by the failure triggered by myassert False is TrueFile "/usr/local/lib/python3.XY/unittest/loader.py", line 426, in _find_test_pathshows the line, method and file where the error triggered by the one triggered by myassert False is TruehappenedTraceback (most recent call last):: all the information shown after this line that is indented to the right shows the calls that led to the failure. The last line is usually the most important one that points to what caused the failure, this is why I like to read it from the bottom up. In this case it is the only one I care about because it is the one I added to cause the failure.ERROR: tests.test_person (unittest.loader._FailedTest.tests.test_person)is a header with information in dot notation about the failing testI think of
tests.test_personas an addresstests │ __init__.py └── test_person.py
GREEN: make it pass
I hold ctrl (Windows/Linux) or option/command (MacOS) on the keyboard and use the mouse to click on
File ".../pumping_python/person/tests/test_person.py", line 2in the terminal, and the Integrated Development Environment (IDE) opens the file with the cursor at the line where the failure happened.I change True to False in
test_person.py1# False is True 2# assert False is True 3assert False is FalseI go back to the terminal to run the test
python3 -m unittestpython -m unittestthe test passes! The terminal shows
------------------------------------------------------ Ran 0 tests in 0.000s NO TESTS RANThis is confusing, since the only way I know the test passed, is because I saw it fail. There has to be a better way. It is why the RED part of the cycle is important, it shows that the test works. For now cue CELEBRATION MUSIC AND DANCE! I am GREEN!!
REFACTOR: make it better
I keep a list of Errors/Exceptions that show up in the terminal as I go through this book to help me know them better. I add AssertionError to
tests/test_person.py1# False is True 2# assert False is True 3assert False is False 4 5 6# Exceptions seen 7# AssertionErrorcomments in Python are written with
#at the beginning, they do not do anything, they are notes for me.I add the new files and folders to git for tracking
git add .the terminal goes back to the command line.
I add a git commit message
git commit --all --message \ 'setup project'the terminal shows a summary of the changes then goes back to the command line.
close the project
I close
tests/test_person.pyI click in the terminal and change directory to the parent of
personcd ....is for the parent of any directory I am in. the terminal shows.../pumping_pythonI am back in the
pumping_pythonfolder.
review
I gave the computer some commands to make a Python Test Driven Development environment
I made some folders
I made some files
I made a failing test
I made the failing test pass
how to view all the commands typed in a terminal
I type history in the terminal to see all the commands I have typed so far
historythe terminal shows
cd person uv init person cd person tree -a -L 2 cat .gitignore cat pyproject.toml cat .python-version cat README.md python3 src/person/__init__.py cat src/person/__init__.py python3 -m unittest mkdir tests tree -a -L 1 touch tests/person.py tree -a -L 2 python3 -m unittest python3 -m unittest touch tests/__init__.py tree -a -L 2 python3 -m unittest mv tests/person.py tests/test_person.py tree -a -L 2 python3 -m unittest python3 -m unittest python3 -m unittest git add . git commit --all --message 'setup project' cd ..cd person uv init person cd person tree cat .gitignore cat pyproject.toml cat .python-version cat README.md python src/person/__init__.py cat src/person/__init__.py python -m unittest mkdir tests tree New-Item tests/person.py tree python -m unittest python -m unittest New-Item tests/__init__.py tree python -m unittest Move-Item tests/person.py tests/test_person.py tree python -m unittest python -m unittest python -m unittest git add . git commit --all --message 'setup project' cd ..these are the commands I used to make a Python Test Driven Development environment
uv init NAME_OF_THE_PROJECT cd NAME_OF_THE_PROJECT mkdir tests touch tests/__init__.py touch tests/test_NAME_OF_THE_PROJECT.pywhere
NAME_OF_THE_PROJECTis the name I give the project.These are the steps I take to make a Python Test Driven Development environment
what is next?
I know how to make a Python Test Driven Development environment manually
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too.