hpr4091 :: Test Driven Development Demo
norrist uses pytest to demonstrate TDD with a trival HPR info app
Hosted by norrist on Monday, 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
- Pytest found the file named
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 withhpr_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 errorAttributeError: 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
andpytest -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