Episode: 4091 Title: HPR4091: Test Driven Development Demo Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr4091/hpr4091.mp3 Transcribed: 2025-10-25 19:31:21 --- This is Hacker Public Radio episode 4,091 from Monday 8th of April 2024. Today's show is entitled Test Driven Development Demo. It is the 20th show of Norrist, and is about 27 minutes long. It carries a clean flag. The summary is Norrist uses Pytist to demonstrate TDD, with a Trivel HP R&F O-A. So I've done a few episodes where I'll write something, some software and Python, and talk a little bit about it. At least a few times I've mentioned using Test Driven Development while I'm doing it. So I thought I'd just give a quick example using a Tribule application about maybe kind of demonstrating a method of doing Test Driven Development. So my last episode was 4075, and I talked about making a Pomodoro timer using Circuit Python, and in that episode I talked a lot about why I like to use Test Driven Development or how it helps me, and I sort of explained what it is if you want to hear that or if you missed it, you can go back and listen, 4075, but just as a quick refresher, what is Test Driven Development, it's where you think about what you're going to do, what you want your code to do, what you want your project to do, and before you write any code, you write a test. And you can do it in a little chunk, you don't have to test the whole application, but you think about a small thing that you want your code to do, and you write a test for it, and you run the test, and obviously the test is going to fail because you haven't written any code yet. So then you write the minimum code to get the test to pass, run the test again, see that it works, and then optionally after you get the test passing, you can rewrite, rerun the code, just make sure the test continues to pass while you're doing the rewrite. So we're going to do our testing with a Python framework called PyTest, P-Y-T-E-S-T. PyTest is just a framework for testing software, it's usually used to test other Python projects, but it doesn't necessarily have to test Python, it can test anything that returns an input. And the good thing about PyTest is it's just Python, so the test is written in Python, so if you understand, or if you know enough Python to write a script or something like that in Python, then you probably know just enough Python to write the PyTest. So it's easy to use if you already know a little bit of Python. It makes heavy use of the Python assert statement, so in Python you can test that something is true by using assert. And I'll show it, I'll have examples later, but if you want to test a variable equals one, you can just say assert this variable equals one. And if it passes, it will, the script will continue, if it fails, the script will abort. And then PyTest, if you use an assert statement, and it passes, your test passes, and if you use an assert statement, your assert fails, obviously your test fails. So PyTest automatically discovers tests, and it's not going to make a lot of sense right now because I haven't even told you what test are or anything. But whenever you go into a directory where your Python project is, you can just run the command PyTest, just like that, you don't have to specify a file pass or anything. And what it will do is it will look for files that start with the name test, and if it finds one that will run it, or it will look for, it will look into the files and look for functions, a Python function that starts with the name test, and it executes those. So what we're going to do is we're going to make two files, one that contains the application that we want to, our trivial application that we're going to be working on, and then a separate file called test underscore something that we'll put our test in. So let's say just as an example project, we want to write a Python script that will print some summary information for the latest published HPR episode. So let's say we want to print the title, the hosts or the correspondence, the date, and the path to the audio file. So if we want to print all this stuff out, we can click on a link and it will take us to the MP3 or AUG or whatever for the file. So we can plan ahead and think about what we might need to do that. So in my mind, the easiest way to do that, and there's a couple ways to do it, but you could scrape the website or something like that, but in my mind, that information is already published for you in a format that's easy to parse in the RSS feed. So what we need to do for our app is we need to figure out how to get the RSS feed, and then we need to figure out how to parse the RSS feed, and we need to know what the URL is. Okay, so let's say our script that's going to pull down the information from the HPR feed. We'll name it hpr-unterscore-info.py, and then we'll make a test, another Python script that's going to contain all our tests, and we'll name that test-unterscore-hpr-unterscore-info.py. Now that's, I like to have my test, the name of the test file reference what it's going to be testing. That's not a requirement, just something I do, it seems to be convention, a lot of people I've seen do it, but it's not a requirement, but it's kind of best practice that way. If you have a bunch of files that start with test, you can just look at the file name and know what it's doing. Okay, so remember with test-driven development, before we even write code, we're going to write our test. So we'll create the file, test-unterscore-hpr-unterscore-info.py, and then we'll put one line in it, and that one line will be import, space, hpr-unterscore-info. That's it. So remember, we're going to name our actual project, hpr-unterscore-info. So in Python, and therefore in Pytest, when you want to reference some code that's in a different file, use the import statement to do that. So whenever we say import-hpr-info, what we're asking it to do is look for a file named hpr-info, and so later we can reference variables or functions or other things that are in that file to do the testing. So we've got our test file with a single import statement in it. Now we can run Pytest. So again, when you run Pytest, you don't have to do anything, specify file pass or anything like that. As long as you're in the directory, it will find the file containing the test, if you named it correctly. So we just run the directory, we just run the tip command, Pytest, and we get an error. The first error we get is module not found, no module named hpr-info. So what it's telling you is it's trying to import hpr-info, but it can't because that file doesn't exist. So we've written our first test, our first test failed. So we need to create the minimum code to get that test to pass. And in this case, the minimum code to get that test to pass will be creating an empty file named hpr-info.py. So we'll just run the touch command and touch hpr-undescore-info.py. Now we run Pytest again, it doesn't really output anything, it doesn't say that we passed or failed, but it doesn't error. So that's good, so we haven't really written a test, but we kind of did the minimum test by importing a file that doesn't exist. We ran the test, it failed, we created an empty file, ran the test again, it passed. Congratulations, we're doing TDD. What we've done is we have confirmed that Pytest, whenever we run Pytest without any file pass or anything, we've confirmed it found the Pytest, underscore hpr-undescore-info.py. Confirm that Pytest found that test file, and we've also confirmed that the test file is trying to import hpr-info.py since that error went away when we created the file. We know that it's looking in the correct place to do the testing. So we'll start writing some tests, some actual software tests. Before I do that, we're going to use the assert statement quite a bit, so just as a quick review for the Python Asserts, you say assert, and then you give it a comparison. So for example, you could say assert1 equals 1, obviously that's true, so that'll pass. Something else you can do is you can say assert a specific function, and you can say assert a specific function equals and then desired output. So what that means is later when we want to test our hpr-info app, we can assert the output of let's say we have a function that prints the hostname, we can say assert the hostname function equals whatever we want it to be. We can also use assert without a comparison operator, and what that does is just verifies that something exists. So if we say assert1, it would equal true, so that assert would pass. The way we can use that is if we have, if we want to verify that something exists, then we can say assert, let's say there's a dictionary that has some files, and we want to assert that it has a specific key in there, we can just say assert dictionary.key, just to make sure that that exists. Okay, so remember one of the first things we actually need to do to figure out, you know, to get the host information for the latest hpr episode, we need to know hpr-feed. So let's let's try to test for that. Let's try to test that we have at least some value for the hpr-feed. So very simple test, we'll just say asserthprinfo.hpr-feed, and this is assuming that we're going to add a variable later called hpr-feed. So when you, the way you reference something that exists in a different module and Python, as you put the module name, and then a dot, and then the object you're referencing. So remember we imported hpr underscore info, that's going to be the script we're writing. So we just write a simple test, asserthprinfo.hpr-feed, then we can run by test. That test will fail because that the object, hprinfo.hpr-feed, doesn't exist yet. So what we can do is we can add in our hpr-feed info, we can add at the very top of the file, we can just put hpr-feed equals, and then whatever the value for the hpr-feed is. And again, we can run by test, this time the test will pass because the hpr-feed variable exists. And we could, if we wanted to, we could even update the test to say, assert that the hprinfo.hpr-feed equals whatever value we want it to be, let's see, if we wanted to be the ockfeed and p3-feed or whatever, we could do that too. So we could, we could test that it exists or we could test that it is a specific value. And then now, in the, when we run by test, it will detect that we've written the test, now we have, and so when we run by test, we can start running it with the dash v flag that usually gives you a little more information. But this time it will say, tell us that it found one test and that test passed. Okay, so now let's plan a function that pulls the hpr-feed and returns some feed data. And we want to test that we get a http200 whenever we pull the hpr-feed. So in our test file, we can write a simple Python function named test underscore, get underscore, show underscore data. So we're testing if we can get the show data. We can define show data equals, and then we're going to say show data equals hpr dot get show data. So what that, what that means is it's going to try to execute a function in hpr underscore info called get show data. And so after we say show data equals, underneath that, we'll have an assert statement that says show data dot status equals 200. So what this means is we're going to run that function and then we're going to expect some information back. Part of that information back is going to be the status. And then when we're asserting that the status is going to equal 200. So now we have another test. We don't have any code yet, but that's okay. We run pi test. We get one test failed and one test passed. So our first test is still passing, that's good, but our new test is failing. That's also good because that's what we expect. You know, part of doing test driven development this way is not just writing and checking passing tests. It's also making sure that you have failing tests. When you expect a test to fail, I'm going to imagine if we had written this now to test that the result is a 200 and we ran the test, expecting it to fail and it passed. We would know that there was something really, really off. So part of the reason you run the test first is to ensure it fails. That way you know that you're actually testing the right thing. Okay, so now that we've written a test to verify the status of the 200, we've verified that test fails. Now we can write, again, the smallest amount of code to get that to pass. So in this episode, I'm not going to review the specific Python code. If you want to see the Python script that we're testing against, kind of been its final result, as well as the test files and its final result. All this stuff will be checked in to my GitLab and links in the show notes. But for now, let's just say through the magic of me doing this two weeks ago, there's a function that pulls down the HPR on feed, gets all the data, and returns it status, and the status is 200. So now we can run the test again, and through the magic of, again, having done this a couple of weeks ago, passes. Now we have two tests passing. Like I said, if you can always just run PyTest without any arguments, but if you want some extra information, you can run PyTest with a dash V flag, and it will show you each individual test, pass or failed, and it'll also give you a percentage. Right now we have two tests, so when one passes, that's 50% when the second one passes, that's 100%. But you can imagine that would be more useful if you have a lot of tests. Okay, so now that we have the feed, and we can assert that we're getting a good result from pulling the feed, let's see if we can figure out if we can get the first episode. So in the HPR underscore info script, we're going to use something called feed parser, Python library called feed parser, and what it does is it returns data as Python dictionaries. So what we want to do is we want to test that we can create a function called get latest entry, and that it will return a dictionary that has a title and a published date. So we'll write a new function in our test file called test underscore get latest entry, and then we'll say latest entry equals HPR info dot get latest entry. So this is assuming that we're going to write a function called get latest entry, and then we're going to assume that that function returns a dictionary. So whenever we run latest entry equals and then the function latest entry will now be a Python dictionary. So then we can assert that there is a dictionary key called title and a dictionary key called published. We don't have to test the values because depending on what day the values will be different, but we want to at least test that the values exist. And we can do that with just kind of a bear assert statement. So we run the test, the test fails. We write the code that actually gets the latest information returns a dictionary. Then we run python-v again. We can see now we have three passing tests. So we'll write one more function to do some testing. Let's say in our actual script, we verify that we can get the information from the HPR feed. So let's kind of write a final test that includes looking for all the information that we actually want to print out. So again, we'll assume that the HPR info script has a function called getentry data. And that function called getentry data, we can pass it the information that we got when we pull the HPR feed. So then what we'll do is we'll say entry data equals, we'll write a test in our test script, entry data equals, and then we'll call the entry data function and pass it the latest entry data. And again, we're expecting a dictionary back that contains, well it contains what we want to test for. So we want to test that there's a title, a host, a published date, and a file. And remember the file is going to be like the path to the archive.org download. And then as a reminder, you know, all the code, if you want to see the code about how this actually works, I'll have a few code snippets in the show notes, but full Python scripts as well as all the tests, all the links to that in my gala project. One little bit of code I will talk about real quick, is at the bottom of the HPR info script, HPR underscore info script. There's a section that starts with if underscore name equals underscore main. And that's real common in Python to have something like that at the bottom of the script. And the reason you do that is you want to differentiate between when you import the script and when you run the script. So what I mean by that is when you import a script from, you know, like from your test file and your importing HPR underscore info, if there's something like print statements or something like that, those things will probably execute just from importing. So what you want to do is you want to separate what you want to happen when you call the script directly versus when you just import it from something else. So it's a little bit of magic. There's reasons, you know, behind all this, which I understand, but I'm not necessarily going to get into here. But you know, the way to do that, the way to separate the code that you want to execute when you call the script, is you put it underneath this if name equals main function. So you'll see that in there. If you look at the code, well, it'll be in the show notes, or if you look at the code on get lab. But the reason it's like that is, you know, that's, this is the bit that calls when you actually execute the script. And what it's going to do, it's going to go through and run code that was in the functions that we've been testing with TDD. So a lot of times that'll happen with TDD. You'll write a whole bunch of functions you want to test. And then finally at the end, the last bit of code you write is tying all these functions you've tested, tying all that together to get the output that you want it. Okay, that's it. It's all I got. Sort of in summary, you know, test TDD, test driven development. It's a programming method where you write tests prior to writing the code. You know, primarily because I like it because it makes me write smaller, easier to understand, more modular code. I'll have links in the show notes. I don't remember to add those. They're not in here. They're not in my notes yet. So as long as I can remember to add them, I'll have links in the show notes. And then just sort of as an exercise, if this is something you're interested in, you can just sort of part two do this. If you want to go do some homework, you can take the script, take the test. And then see if you can figure out how to write a test to verify that whenever the script returns a date, that it's a possible date, that it's a date that makes sense. You can test that it's actually a weekday. You know, HBR only comes out Monday through Friday, so you can test. It wouldn't be hard if you know a little bit of Python to write a test to verify that the date that's being returned is actually a weekday. Or you could test, maybe you could figure out how to pull in the correspondence page from HBR, do a little bit of web parsing, and then you can verify that the script that we wrote when it returns a host, that that host is can verify that it's on the correspondence page. There's all kinds of other things you can do. Again, this was just an example. I made up, I thought it would be a good way to kind of demo test driven development. That's it, that's all I got. I'll see you guys next time. You have been listening to Hacker Public Radio at Hacker Public Radio does work. Today's show was contributed by a HBR listener like yourself. If you ever thought of recording broadcast, you click on our contribute link to find out how easy it really is. Hosting for HBR has been kindly provided by an onsthost.com, the Internet Archive and our Sync.net. On the Satellite Stated, today's show is released under Creative Commons, Attribution 4.0 International License.