how to make a python test driven development environment


preview

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 be know these commands better

mkdir
cd
touch
echo
cat
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install --requirement requirements.txt
pytest-watch
deactivate
history

questions about making a Python Test Driven Development Environment

Here are questions you can answer after going through this chapter


requirements

Windows requirements

Linux/Windows Subsystem for Linux requirements

Attention

Do this only if you are using Linux or Windows Subsystem for Linux. MacOS users should not do this section

  • Open a terminal then type this to update the Linux package manager

    sudo apt update
    

    Tip

    you can do a full upgrade if you want

    sudo apt full-upgrade --yes
    
  • type this in the terminal to install Python

    sudo apt install python3 python3-venv --yes
    

how to make a Python Test Driven Development environment manually

I choose magic as the name for this project


how to make a directory for the project


how to change directory to the project

I try cd again

cd magic

the terminal shows I am now in the magic folder I just made in the pumping_python folder

.../pumping_python/magic

how to run a Python program

I use Python to run the magic program

python3 src/magic.py

the terminal shows

python3: can't open file '.../pumping_python/magic/src/magic.py': [Errno 2] No such file or directory

the computer cannot find the program because it does not exist, yet


how to make a directory for the source code


how to make an empty file

  • I use touch to make an empty file in the src folder

    touch src/magic.py
    

    the terminal goes back to the command line

  • I use tree to see what folders and files I now have

    tree
    

    the terminal shows

    .
    └── src
        └── magic.py
    

    touch is a program that makes an empty file with the name. I can give it the directory I want to put the file in as part of the name, in this case touch src/magic.py makes a file named magic.py in the src folder

  • I try to run the magic program again

    python3 src/magic.py
    

    the terminal goes back to the command line. Success! Even though magic.py does not do anything because there is no code in it, I can successfully run it because it exists.


test_failure

how to manually run tests

I do not have any tests yet, so there is nothing to run


how to make a directory for the tests

  • I make a child folder to keep the tests separate from the actual program

    mkdir tests
    

    the terminal goes back to the command line

  • I use tree to see what my project looks like

    tree
    

    the terminal shows

    .
    ├── src
       └── magic.py
    └── tests
    

how to make a Python file to hold the tests in the ‘tests’ folder

  • I use touch to add an empty file to the tests directory for the actual test

    touch tests/magic.py
    

    the terminal goes back to the command line

  • I use tree to see what the project looks like so far

    tree
    

    the terminal shows

    .
    ├── src
       └── magic.py
    └── tests
        └── magic.py
    
  • I run the test again

    python3 -m unittest
    

    the terminal shows

    NO TESTS RAN
    

RED: make it fail

how to make the tests a Python package

  • I use touch to add an empty file with the name __init__.py in the tests folder

    Attention

    use 2 underscores (__) before and after init for __init__.py not _init_.py

    touch tests/__init__.py
    

    the terminal goes back to the command line

  • I run the tree command to see what changed

    tree
    

    the terminal shows

    .
    ├── src
       └── magic.py
    └── tests
        ├── __init__.py
        └── magic.py
    
  • I try to run the tests again

    python3 -m unittest
    

    the terminal shows

    NO TESTS RAN
    

    I need to tell Python that magic.py in the tests folder is a test file

  • I close magic.py in the editor of the Integrated Development Environment (IDE)

    Caution

    if you do not close magic.py you will end up with 3 files in the tests folder after the next step (instead of 2), because the Auto Save feature (enabled earlier) will save the original file after you change its name

how to change the name of a file

  • I use the mv program to change the name of magic.py in the tests folder to test_magic.py

    mv tests/magic.py tests/test_magic.py
    

    the terminal goes back to the command line

  • I use tree with the -L option to see what I have so far

    tree -L 2
    

    the terminal shows

    Note

    if you do not see __pycache__ in the list do not worry, the important thing is that you renamed magic.py to test_magic.py for unittest to find the test

    .
    ├── src
       └── magic.py
    └── tests
        ├── __init__.py
        ├── __pycache__
        └── test_magic.py
    

    the -L option tells tree how deep to go when showing the folders and files, I use 2 to make it show only the first level of contents of the child folders

  • I run the tests again

    python3 -m unittest
    

    the terminal shows AssertionError

    F
    =============================================================
    FAIL: test_failure (tests.test_magic.TestMagic.test_failure)
    -------------------------------------------------------------
    Traceback (most recent call last):
      File "...pumping_python/magic/tests/test_magic.py", line 7, in test_failure
        self.assertFalse(True)
        ~~~~~~~~~~~~~~~~^^^^^^
    AssertionError: True is not false
    
    -------------------------------------------------------------
    Ran 1 test in A.XYZs
    
    FAILED (failures=1)
    

    Important

    I can use any name for the test file but it must start with test_ or 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 (failures=1) the number of failures

    • Ran 1 test in A.XYZs the number of tests it ran and how long they took

    • AssertionError: True is not false the Error (Exception) that happened and its message, in this case AssertionError because True is not False

    • self.assertFalse(True) the line of code that caused AssertionError

    • ~~~~~~~~~~~~~~~~^^^^^^ points to the part of the line above that Python thinks caused the error

    • File ".../magic/tests/test_magic.py", line 7, in test_failure the line number of the code that caused the error and the location of the file where it is

    • Traceback (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, this is why I like to read it from the bottom up

    • FAIL: test_failure (tests.test_magic.TestMagic.test_failure) is a header with information in dot notation about the failing test method

      • tests.test_magic.TestMagic.test_failure is the location of the failing test

      • tests is the tests folder

      • test_magic is the test_magic.py file in the tests directory

      • TestMagic is the class defined on line 4 in test_magic.py

      • test_failure is the method (function) defined on line 6 in test_magic.py

    • F shows a failure

  • I hold ctrl (Windows/Linux) or option or command (MacOS) on the keyboard and use the mouse to click on File ".../pumping_python/magic/tests/test_magic.py", line 7 in the terminal, and the Integrated Development Environment (IDE) opens the file in the editor with the cursor at the line where the failure happened

GREEN: make it pass

I change True to False on line 7 of test_magic.py in the editor

7        self.assertFalse(False)

I run the test again in the terminal

python3 -m unittest

the test passes! The terminal shows

.
------------------------------------------------------
Ran 1 test in A.XYZs

OK

cue CELEBRATION MUSIC AND DANCE! I am GREEN!!

REFACTOR: make it better

Keep a list of Errors/Exceptions that show up in the terminal as you go through this book to know them better, it helps when you run into them later. I add a list with AssertionError in test_magic.py in the editor

 1import unittest
 2
 3
 4class TestMagic(unittest.TestCase):
 5
 6    def test_failure(self):
 7        self.assertFalse(True)
 8
 9
10# Exceptions seen
11# AssertionError

I ran python3 -m unittest a few times to see the test fail, I ran python3 -m unittest again to see the test pass. I will have to run python3 -m unittest again when I add any code, to make sure tests that were passing do not start failing and that the new code I add does what I want.

This means I have to run python3 -m unittest for each part of the Test Driven Development cycle or any time there is a code change.

I do not want to type python3 -m unittest again, it is better for a computer program to run the tests so that I do not repeat myself.


how to run the tests automatically

I can use pytest-watch to run tests automatically. It is a Python program that automatically runs pytest any time a Python file changes in the folder it is looking at, this means it will run the tests for me every time I make a change.

pytest is a Python package like unittest, it is not part of the Python standard library

I type it in the terminal

pytest-watch

the terminal shows

command not found: pytest-watch

I need to install pytest-watch for the computer to use it. Next, I set up a virtual environment to keep programs my project needs in one place


what is a virtual environment?

I can install pytest-watch globally (for the entire computer), which means it will always be available to any project on the computer, but a better way would be to put it in a virtual environment so that it is installed only for this project.

A virtual environment is a separate folder where I can install Python packages that my project needs. This helps me keep things that belong to the project in one place, separate from other things on the computer.

It means I can have a separate virtual environment for every project with only the programs that the project needs. This helps if I decide to package the program to send to someone else, because everything needed by the project is in one place.


how to make a virtual environment
  • I make a virtual environment with the venv module from the Python standard library

    python3 -m venv .venv
    

    the terminal goes back to the command line

  • I run tree

    tree -L 2
    

    the terminal shows

    .
    ├── src
       └── magic.py
    └── tests
        ├── __init__.py
        ├── __pycache__
        └── test_magic.py
    

    it does not look like anything changed. This is because the . in front of .venv means the folder is hidden

  • I try tree again with another option to see what changed

    tree -a -L 2
    

    the terminal shows

    .
    ├── src
       └── magic.py
    ├── tests
       ├── __init__.py
       ├── __pycache__
       └── test_magic.py
    └── .venv
        ├── bin
        ├── .gitignore
        ├── include
        ├── lib
        ├── lib64 -> lib
        └── pyvenv.cfg
    

    there is now a folder named .venv for the virtual environment

    • the -a option makes tree show all files that are listed including hidden files and folders

    • the -L option tells tree how deep to go when showing the folders and files, I use 2 to keep it to the first level of what is in the children folders of magic


how to activate a virtual environment

how to see what packages are installed in a virtual environment

I use the Python package manager (pip) to see what Python packages are installed in the virtual environment I just made

pip list

the terminal shows

Package Version
------- -------
pip     x.y

how to write text to a file

I want to make a file where I can list all the Python packages for my project as a way to document it and have pip install the programs listed in the file

  • I can write text to a file with the echo program, it shows whatever it is given as an argument, on the screen (standard output (stdout)) for example

    echo "pytest-watch"
    

    the terminal shows

    pytest-watch
    
  • I can also use echo to add text to a file, I use it to make the requirements file with pytest-watch as what is inside it

    echo "pytest-watch" > requirements.txt
    
  • I run tree to see what the project looks like now

    tree -a -L 2
    

    the terminal shows

    .
    ├── requirements.txt
    ├── src
       └── magic.py
    ├── tests
       ├── __init__.py
       ├── __pycache__
       └── test_magic.py
    └── .venv
        ├── bin
        ├── .gitignore
        ├── include
        ├── lib
        ├── lib64 -> lib
        └── pyvenv.cfg
    

    requirements.txt is now in the magic folder


how to see what is inside a file

I can use the cat program to see what is inside a file. I use it to make sure my requirements.txt has pytest-watch inside it

cat requirements.txt

the terminal shows

pytest-watch

life is good!


how to install Python packages in a virtual environment
  • I use pip to install pytest-watch from the requirements file

    python3 -m pip install --requirement requirements.txt
    
    • --requirement is an option that can be given to the install argument for Python packages in a given file

    • requirements.txt is the name of the given file

    the terminal shows programs being downloaded and installed, and when I do not have the latest version of pip installed, it shows this at the end

    [notice] A new release of pip is available: XY.Z -> AB.C
    [notice] To update, run: pip install --upgrade pip
    

how to upgrade the Python package manager in a virtual environment
  • I upgrade pip to the latest version. I recommend you do this every time you are in a virtual environment, it is good practice to update package managers to the latest version available

    python3 -m pip install --upgrade pip
    

    the terminal shows

    ...
    Installing collected packages: pip
      Attempting uninstall: pip
        Found existing installation: pip XY.Z
        Uninstalling pip-XY.Z:
          Successfully uninstalled pip-XY.Z
    Successfully installed pip-AB.C
    

    Note

    I can also tell pip to install pytest-watch directly without using a requirements file, the problem is it will not document what programs my project needs. I would either have to remember later or use pip list. It also does not help someone else who is trying to run my project later, know what programs it needs without me

    python3 -m pip install pytest-watch
    
  • I check what Python packages are now installed in the virtual environment

    pip list
    

    the terminal shows

    Package      Version
    ------------ -------
    colorama     x.y.z
    docopt       x.y.z
    iniconfig    x.y.z
    packaging    x.y
    pip          x.y
    pluggy       x.y.z
    Pygments     x.y.z
    pytest       x.y.z
    pytest-watch x.y.z
    watchdog     x.y.z
    

    pytest-watch is in the list. Yes!

    Tip

    imagine that the pytest-watch project also has a requirements file with colorama, docopt, iniconfig, packaging, pluggy, Pygments, pytest and watchdog as programs that it needs to run and they got installed when I asked pip to install pytest-watch from the requirements.txt file


how to run the tests automatically in a virtual environment

I try to run the tests again

pytest-watch

and it shows results without going back to the command line

================== test session starts===================
...
rootdir: .../magic
collected 1 item

tests/test_magic.py .                              [100%]

=============== 1 passed in X.YZs =======================

how to open the test file in the editor from the terminal
  • I hold ctrl (Windows/Linux) or option or command (MacOS) on the keyboard and click on tests/test_magic.py to place the cursor in the editor of the Integrated Development Environment (IDE), then I change False to True on line 7

    7      self.assertFalse(True)
    

    the terminal shows AssertionError

    ====================================== FAILURES =======================================
    _______________________________ TestMagic.test_failure ________________________________
    
    self = <tests.test_magic.TestMagic testMethod=test_failure>
    
        def test_failure(self):
    >       self.assertFalse(True)
    E       AssertionError: True is not false
    
    tests/test_magic.py:7: AssertionError
    ============================== short test summary info ================================
    FAILED tests/test_magic.py::TestMagic::test_failure - AssertionError: True is not false
    ================================= 1 failed in X.YZs ===================================
    

    I hold ctrl (Windows/Linux) or option or command (MacOS) on the keyboard and click on tests/test_magic.py:7 to place the cursor in the editor of the Integrated Development Environment (IDE), then I change True back to False in test_magic.py

    7      self.assertFalse(False)
    

    the test passes and I can write the rest of the code for the project as the tests run automatically in response to any change I make


how to stop the automated tests

I exit the tests in the terminal with ctrl+c on the keyboard


how to deactivate a virtual environment

review

I gave the computer some commands to make a Python Test Driven Development environment. I made some folders and files, successfully wrote a failing test, made it pass, then made the tests run automatically

how to view all the commands I typed in a terminal


How many questions can you answer after going through this chapter?


what is next?

You have seen me make a Test Driven Development environment for a project called magic on any Linux, Windows with Windows Subsystem for Linux or MacOS computers. Would you like to test AssertionError next?


rate pumping python

If this has been a 7 star experience for you, please leave a 5 star review. It helps other people get into the book too