Site Map - skip to main content

Hacker Public Radio

Your ideas, projects, opinions - podcasted.

New episodes every weekday Monday through Friday.
This page was generated by The HPR Robot at


hpr4091 :: Test Driven Development Demo

norrist uses pytest to demonstrate TDD with a trival HPR info app

<< First, < Previous, , Latest >>

Hosted by norrist on 2024-04-08 is flagged as Clean and is released under a CC-BY-SA license.
python, testing, pytest. (Be the first).
The show is available on the Internet Archive at: https://archive.org/details/hpr4091

Listen in ogg, spx, or mp3 format. Play now:

Duration: 00:27:00

general.

Test Driven Development Demo with PyTest

TDD

  • Discussed in hpr4075
  • Write a new test and run it. It should fail.
  • Write the minimal code that will pass the test
  • Optionally - refactor the code while ensure the tests continue to pass

PyTest

  • Framework for writing software tests with python
  • Normally used to test python projects, but could test any software that python can launch return input.
  • if you can write python, you can write tests in PyTest.
  • python assert - check that something is true

Test Discovery

  • Files named test*
  • Functions named test*

Demo Project

  • Trivial app as a demo
  • Print a summary of the latest HPR Episode
  • Title, Host, Date, Audio File
  • How do we get the latest show data
    • RSS feed
    • Feed parser
    • Feed URL

The pytest setup

  • The python script we want to test will be named hpr_info.py
  • The test will be in a file will be named test_hpr_info.py

test_hpr_info.py

import hpr_info

Run pytest

ModuleNotFoundError: No module named 'hpr_info'
  • We have written our first failing test.
  • The minimum code to get pytest to pass is to create an empty file
  • touch hpr_info.py

Run pytest again

 pytest
============================= test session starts ==============================
platform linux -- Python 3.11.8, pytest-7.4.4, pluggy-1.4.0
rootdir: /tmp/Demo
collected 0 items

What just happened

  • We created a file named test_hpr_info.py with a single line to import hpr_info
  • We ran pytest and it failed because hpr_info.py did not exist
  • We created hpr_info.py and pytest ran without an error.
  • This means we confirmed:
    • Pytest found the file named test_hpr_info.py and tried to execute its tests
    • The import line is looking for a file named hpr_info.py

Python Assert

  • In python, assert tests if a statement is true
  • For example
asert 1==1

In pytest, we can use assert to check a function returns a specific value

assert module.function() == "Desired Output"

Without doing a comparison operator, we can also use assert to check if something exists without specifying a specific value

assert dictionary.key

Adding a Test

  • Import hpr_info will allow us to test functions inside hpr_info.py
  • We can reference functions inside hpr_info.py by prepending the name with hpr_info. for example
hpr_info.HPR_FEED
  • The first step in finding the latest HPR episode is fetching a copy of the feed.
  • Lets add a test to make sure the HPR feed is defined
import hpr_info


def test_hpr_feed_url():
    assert hpr_info.HPR_FEED == "https://hackerpublicradio.org/hpr_ogg_rss.php"

pytest again

  • Lets run pytest again and we get the error AttributeError: module 'hpr_info' has no attribute 'HPR_FEED'
  • So lets add the just enough code hpr_info.py to get the test to pass
HPR_FEED = "https://hackerpublicradio.org/hpr_ogg_rss.php"
  • Run pytest again and we get 1 passed indicating the pytest found 1 test which passed
  • Hooray, we are doing TDD

Next Test - Parsing the feed

  • lets plan a function that pulls the HPR feed and returns the feed data.
  • We can test that the result of fetching the feed is a HTTP 200
def test_get_show_data():
    show_data = hpr_info.get_show_data()
    assert show_data.status == 200
  • Now when we run pytest we get 1 failed, 1 passed and we can see the error AttributeError: module 'hpr_info' has no attribute 'get_show_data'
  • Lets write the code to get the new test to pass.
  • We will use the feedparser python module to make it easier to parse the rss feed.
  • After we add the import and the new function, hpr_info.py looks like this
import feedparser

HPR_FEED = "https://hackerpublicradio.org/hpr_ogg_rss.php"


def get_show_data():
    showdata = feedparser.parse(HPR_FEED)
    return showdata

  • Lets run pytest again. When I have more than one test, I like to add the -v flag so I can see each test as it runs.
test_hpr_info.py::test_hpr_feed_url PASSED                                                                                               [ 50%]
test_hpr_info.py::test_get_show_data PASSED                                                                                              [100%]

Next Test - Get the most recent episode from the feed

  • Now that we have the feed, lets test getting the first episode.
  • feedparser entries are dictionaries.
  • Lets test what the function returns to make sure it looks like a rss feed entry.
def test_get_latest_entry():
    latest_entry = hpr_info.get_latest_entry()
    assert latest_entry["title"]
    assert latest_entry["published"]

  • After we verify the test fails, we can write the code to rerun the newest entry data to hpr_info.py and pytest -v will show 3 passing tests.
def get_latest_entry():
    showdata = get_show_data()
    return showdata["entries"][0]

Final Test

  • Lets test a function to see if it returns the values we want to print.
  • We don't test for specific values, just that the data exists.
def test_get_entry_data():
    entry_data = hpr_info.get_entry_data(hpr_info.get_latest_entry())
    assert entry_data["title"]
    assert entry_data["host"]
    assert entry_data["published"]
    assert entry_data["file"]

And then code to get the test to pass

def get_entry_data(entry):
    for link in entry["links"]:
        if link.get("rel") == "enclosure":
            enclosure = link.get("href")

    return {
        "title": entry["title"],
        "host": entry["authors"][0]["name"],
        "published": entry["published"],
        "file": enclosure,
    }

Finish the HPR info script.

Now that we have tested that we can, get all the info we want from the most recent episode lets add the last bit of code to hpr_info.py to print the episode info

if __name__ == "__main__":
    most_recent_show = get_entry_data(get_latest_entry())
    print()
    print(f"Most Recent HPR Episode")
    for x in most_recent_show:
        print(f"{x}: {most_recent_show.get(x)}")

if __name__ == "__main__": ensures code inside this block will only run when the script is called directly, and not when imported by test_hpr_info.py

Summary

  • TDD is a programming method where you write tests prior to writing code.
  • TDD forces me to write smaller functions and more modular code.
  • Link to HPR info script and tests - TODO
  • Additional tests to add
    • Check date is the most recent weekday
    • Check this the host is listed on corespondents page
    • Check others.
  • Project Files - https://gitlab.com/norrist/hpr-pytest-demo

Comments

Subscribe to the comments RSS feed.

Leave Comment

Note to Verbose Commenters
If you can't fit everything you want to say in the comment below then you really should record a response show instead.

Note to Spammers
All comments are moderated. All links are checked by humans. We strip out all html. Feel free to record a show about yourself, or your industry, or any other topic we may find interesting. We also check shows for spam :).

Provide feedback
Your Name/Handle:
Title:
Comment:
Anti Spam Question: What does the letter P in HPR stand for?