Pytest

A hard truth in this world is that all of humanity is kind dumb. Even you— the coder — are dumb. We will cover how to harden your package against your dumb users, and then how to protect your package from your dumb self.

Protecting Yourself from the User

Let's focus on how we can make the duration method more stable. First thing first, let's add type hints:

    def duration(self, distance: int, speed: int) -> int:
        return (distance/speed)*60

Next, we can add raise exceptions for user inputs that may not make sense. For example, if we wanted to prevent the user from inputting a speed over 100 mph we would do the following:

    def duration(self, distance: int, speed: int) -> int:
        if speed > 100:
            raise Exception("Slow down!")

        return (distance/speed)*60

Protecting Yourself from Yourself

Now that we have protected our immaculate function from the users, we need to protect it from ourselves. Basically, we are going to see if the method works as we expect.

Make sure you are in your poetry shell, and then we are going to first install pytest:

pip install pytest

If you recall, poetry automatically created a tests folder. In that folder, we are going to put our tests. In order for pytest to test the tests, each file needed to be prepended with test_. So let's add tests for our RoadTrip class.

This will be the directory structure:

roadtrip
├── pyproject.toml
├── README.md
├── roadtrip
│   └── __init__.py
│   └── trip.py
└── tests
    └── __init__.py
    └── test_roadtrip.py

One thing we would want to do is make sure that the output of duration is what we expect it to be. If we are going 60 mph and going 60 miles, then it should take 60 minutes to get there.

Let's write our duration function to do that and put it into test_roadtrip.py:

import roadtrip

def test_duration():
    trip1 = roadtrip.RoadTrip()
    assert 60.0 == trip1.duration(60, 60)

Now in the root folder of our poetry project run pytest like so:

pytest

It will run the test and should return something like the below if they pass:

========================== test session starts =============================
platform darwin -- Python 3.9.7, pytest-8.0.0, pluggy-1.4.0
rootdir: /Users/corydonbaylor/Documents/github/python_package/roadtrip
collected 1 item                                                                                                                 

tests/test_roadtrip.py .                                                                                                   [100%]

========================== 1 passed in 0.02s ================================

Now let us write a test to make sure that our exception works:

def test_exception():

    # creating a instance of roadtrip
    trip1 = roadtrip.RoadTrip()

    # this will capture the execption as exc
    with pytest.raises(Exception) as exc:
        trip1.duration(60, 160)

    # this will make sure that exc is equal to what it should be
    assert str(exc.value) == "Slow down!"