Come on in, the Water’s Fine: Making Python More Approachable
Published November 3, 2022
This video features Melanie Arbor at DjangoCon US 2019 in San Diego, California, USA.
DjangoCon 2019 - The Unspeakable Horror of Discovering You Didn't Write Tests by Melanie Crutchfield
PSA: if you don't write tests, you'll instead write code for a time machine to go back & train a band of feisty raccoons to dump grape jelly on the head of your past self because you'll have realized she is a terrible, lazy, awful person.
How do I know?
Because I didn't write tests.
This talk was presented at: https://2019.djangocon.us/talks/the-unspeakable-horror-of-discovering-t/
LINKS:
Follow Melanie Crutchfield 👇
On Twitter: https://twitter.com/hellomelaniec
Official homepage: https://melaniecrutchfield.com
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Intro music: "This Is How We Quirk It" by Avocado Junkie.
Video production by Confreaks TV.
Captions by White Coat Captioning.
Melanie Crutchfield recounts discovering that her Django project, FiveUp, had no tests after she had postponed learning testing. She explains how pytest, pytest-django, Selenium, and pytest-cov can reveal untested code and automate checks from browser behavior to individual scheduling functions. Functional tests verify that a site behaves as users expect, while unit tests check smaller pieces of application logic; the terminology varies, but both help catch regressions. She argues that testing is worthwhile not because it prevents every bug, but because it provides a safety net, mental freedom, and confidence to change code.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
I'm super glad that we're bonded already. Like this meals makes me feel good because I'm guessing some of you are hangry, but now that we have this thing that we share, you're not going to attack me, um, which is good because I cannot defend myself So uh this is me. I'm Hello Melanie C almost everywhere on the internet, so feel free to stalk me. Don't nope, taking that back right away. That was close. So, and I'm gonna talk to you about testing, about a cautionary tale of discovering that you did not write tests. So it begins. Um do you ever have those things uh Where you think like I'm totally gonna learn that thing later.
Um, but you also have this feeling like later is Like never is what that means. It's a it's a word that you say that means another word. Hands, yes? People? Yep. That guy over there doesn't feel vulnerable enough yet to raise his hand. That's okay. I see you. It's all right. Um So I am a community taught developer in that everything that I learned was completely guided and informed. by the San Diego Python User Group and San Diego PyLadies, which was a really wonderful experience for me because it made a career in tech possible for me. It made learning accessible. The only downside though
is that there's no one to make you do the things that you should do, but don't want to do. Um kind of no one there to like give you your developer vitamins, so to speak, right? So when I started um learning Python, There were a bunch of people that said, um, you should learn Django. Like Django's a really good thing. Um and after a bit of like kind of ignoring and running away, um, I did. And I made this thing called Five Up. Um it's Happy texts every day, um, unless you don't live in the United States or you have a weird carrier. I'm sorry, international stuff is hard, and I'm going to learn that later. So, but the point here is
that I took my developer vitamins, I learned something new, and that's very fortunate because that's how I make my living today. So after that, my sweet, sweet community, so just so that I learned testing. I didn't know exactly what that entailed, but I did know that it sounded scary. And so I told them I would for sure completely learn that later on after not. learning it at all. Um so I I kind of limped along like this. Like I absorbed the little bits and pieces of testing um but mostly kind of ran away from testing. Until I got a fancy job that required
testing. And not like Sabrina, the engineering manager, is gonna be just disappointed in you if you don't do your tests, but rather like you're gonna try to deploy and your deploy is gonna barf on your face. Jenkins. Anyone? The disapproving scowl of Jenkins. It doesn't actually ever scowl if you've never used it. He's just a very pleasant person ruining it. your day. Anyway. So I took my medicine and something weird happened. I got addicted. to testing. Testing my code is the only thing that really makes sense to me now. It's kind of like When you start eating kale because Michelle Obama was like, kale is super good for you, and you just like
want to make her proud and happy. um in her heart. And uh then one day you go to a restaurant and you order a kale salad because it sounds good. And then you think, Michelle Obama, you beautiful dynamic trickster, because now you like kale, which by the way, if you want to know where my favorite kale salad is in San Diego, you can let me know later and I'll I'll totally tell you. It's good. Kale is good. What? So that was me. O'Reilly was Michelle Obbs. Testing was kale. I was Me, because that makes sense. And all of a sudden, I wanted that disgusting vegetable that's true. I think some of you like kale
like from birth because no one's agreeing with me that it's actually revolting, but anyway. So one day I realized it had been a while since I had looked at the five up code, um, and I should probably go in like Spruce stuff up a little bit. Um so I opened it up and I was thinking like, okay, I'll upgrade a Python 3 and then I'll run the tests and see what breaks then I'll go to fix oh my god like there was not a single test like not even like one little disgusting kale leaf to work with Nothing, which is like genuinely terrifying at this point. And this was not going to work with me because I am now the testing spoiled monster you see before you So now that we've got Melanie, the testing hero's origin
story out of the way, let's do this. So the first thing we're gonna do is bring in some buddies because Lord knows I should not be left to my own devices here. And so here are the majors. We're gonna install PyTest and PyTest Django. Selenium and PyTestCov. These are handy because you can just pip install all of them. PyTest here is just kind of like a little bit of a sweeter way to use uh to do testing as compared to Python's built-in unit test. You can Google that and look at all the wonders of it, but you can trust me on that also. Py test Django makes PyTest play on I see with Django. Selenium is going to allow our tests to pretend to be a user in a live browser.
Pretty amazing. And then lastly, my favorite magic here is PyTestCov, which is going to show us which code we've tested and which we haven't. So let's talk about that last one for just a mo. PyTestCov is a plugin that implements coverage. py in a way that like works nicely with PyTest. which is why it's PyTestCov. Coverage. py is a tool for measuring how much of your code is run when your tests are run. This is what I mean when I say code coverage, in case some of you were like, stop just saying words at us, geez. So you want to be
a little bit cautious here though, because PyTestCov is only going to tell you that your code is being executed. It's not going to tell you that you are executing it in a way that is all at all reasonable. So you can think of it as a guide as in a ruler, not a guide like a wilderness guide who's going to tell you that you're going to put a bee's nest thing on your face. I don't think everyone hikes the way I hike. Anyway, but the measurement itself is pretty cool. handy. So before we begin writing any tests, we're going to try to get an idea of what we're up against, and we can do that using PyTest. This command here runs PyTest.
It asks for a coverage report to be printed to the terminal. And then it asks basically PyTest to skip over anything that's already covered. 100%. So if there's a file that's already covered, it just ignores it. And then it's also going to print out the exact lines that were not run for us. So you can see at this point that our coverage is pretty abysmal. And whatever coverage we do have is probably just because it's an empty file, like an empty init. py file would fall into the that example. So our code coverage is currently at 28%, leaving 72% of our code untested and able to eat our users when we're not looking, which is not ideal. I don't know, just in case any
Anyone was doing that eating the user thing. So testing isn't going to help us like, it's not going to avoid all of the bugs. You can't catch everything, but it is helpful to push us in the right direction. So now that we've got all of our tools, let's write our first test. It's going to be a functional test. So there's a couple of words that you'll hear thrown around with testing and Django specifically. And the terms that you'll hear are functional test and unit test. test. We're going to get to unit tests a little bit later, but here's what I want to say about functional tests. They have nothing to do with functions. as in like deaf fancy thing.
Functions are very common in Python, so it makes sense that you would think like a functional test Has something to do with my functions, but surprise they don't and everything's confusing. Yay. So but here's what functional tests are about. It's kind of asking the question Does my code do what I intend it to do? Does it function at all? Right? And since we're working in Django , a good example of this would be asking. like, okay, if I visit this page and I click this thing, does it do what I anticipate it will do? Right? Without testing. You're forced to kind of, you know, maybe spin up your server, go visit the site, right? And click everything.
You know, just see what happens. But you also better hope that you're going to click everything that your user will click and in the ways that they will click it, which interestingly enough, users are especially adept at finding the one thing that you did not click. It's up to you if that's what you want to do, but um I'm gonna say pass on that one. So we are going to manually ditch the um clicking around and automate it instead. because we are lazy smart, which is a term that I've come up with that you're welcome to put on your resume. We're going to start with a super basic test. And if we kind of look around here at the code, we're going to see that if our site is functioning properly, we should be able to find the title page
and find some concept. So let's write a test to see if Selenium, one of the fancy tools that we installed, can find them. I do want to mention that Selenium needs a little bit more setup with a thing called Gecko Driver, and doing that can be like a little bit wobbly. I don't have notes on it in this talk, so So you'll have to Google it. But once you set it up, because you are brave and smart, you will write tests kind of like this. So here, Selenium is going to launch a browser, visit the page, and then poke around to see if it can find the elements that we're looking for. I personally think this Is amazing. So in real life, you'll be doing things that are much more complicated than this.
But this is a nice example to kind of get our bearings. So PyTest. This is one of the things I like about PyTest is that you can just use the word assert. So we're going to assert that five up is in the browser title. And then we're going to look in the browser text. to see if we can find how it works. And then we're going to look and see if we can find a link on the page called Frog Fart Party. Rats, our test failed. You can see up at the top that we have an F. And the reason it failed is because there is no link. Um with a text frog part party. Um, 'cause that would be really weird. And I'm a little surprised that some of you looked like that would be normal and you would expect me to have that on my website.
We just met and already your standards. Are really quite low. So, but if we take it out, the test will pass, which PyTest expresses gleefully with a dot. Take that home and put it on your fridge. So we'll see also that our coverage has gone up at this point. Which is great. Um now the bits here where we're like setting up the driver and doing all the stuff, like this is stuff that we will repeat in And it will get laborious if we do it every time. So let's set up a quick little helper here. Again, don't remember about remembering this stuff. Like that's what Google Google's for, right? But this is going to be an important part for our test that we're going to do next.
So just kind of keep that in mind that we're doing some imports here from Context Lib and Django and Selenium. And then we're going to make a setup class and or class method rather and a teardown. And so the point here with stuff like this is that we're going to do a little bit of prep work and it's going to allow us to power through tests later. Alrighty. So this sweet little test is going to check and see if we can log in. So it's kind of important. um that your users can log in. And the nice thing about this is that um it's gonna hit our HTML page, right? So the page has to exist. We have to know that it runs But it's also going to hit this form and it's going to have to interact with the user.
So there's several things that we're testing all at once, which is pretty And it kind of illustrates the power of testing because if I change my CSS code, I should still be able to interact with the form, right? The form shouldn't just blow up because I made it purple. And same thing with our users. So kind of cool. So stepping through the code, we'll see that we made a user here. And then we set up the browser that we set up in our utility. And then we're going to find the username field. And Selenium allows you to enter an email address in there, which is pretty cool. And then we'll find the password element and do the same thing. And then we can click submit.
And then if all of that worked properly, we should be able to find high username in the text. And if it does. We get two dots. Holy smokes, we're gonna need a bigger refrigerator. Uh so this is why to me it's so distra disturbing to not have tests because without them you just kind of have to hope uh that whatever change you made didn't break anything. And as your project grows and it's getting larger, if you don't have tests, then you really have to start To rely on yourself to be some kind of like super omniscient mega developer or whatever. And I know that that's not me. That might be you. Also let's not talk later because I don't like you already.
Just kidding, you can be smart all you want, but also please let's not talk. Anyway, at least not about code. We can talk about cats. I'm giving a talk here about stuff. Here we go. So now when we run our tests with that command that we did earlier, you can see that we've really bumped up the coverage here. And then now we'll just go back and see what else isn't covered and work on that. So let's move on to unit tests. Now the phrase unit tests made me think that there was something in Python like called a unit test unit. Like maybe it's a like entire Python file or it's a function or it's something. But it it doesn't mean that. What it really means is like
chunk testing or you know smidget testing or nibblet testing. Just like a bit. Like we're gonna test a bit of code. splotch, uh a blob, scrap, um, a morsel. Anyone else can join in here. A bang, that's a thing. Did you know that? An apportionment. I like that this Really cool. Anyway, so um also names are completely arbitrary and nothing means anything because everyone argues about what a functional test is and what a unit test is. So whatever we decide here, someone outside, especially that super knowledgeable developer, is going to argue with us about it. So w they argue about unit tests and and functional tests and we can use all of these words.
There's also end-to-end tests, um, structural tests, pregnancy tests and integration tests and cake testers. Um Not all of those are related, but you know. Um at any rate, don't try to know stuff. Like that's the point here. Um knowing stuff is impossible. I mean look at that like sad, really dapper man Like he's so, so confused. Like, let's just do stuff. Um So I'm going to think of unit tests as making sure like the models and the functions and the behind-the-scenes stuff works properly. So in FiveUps case, one of the things that happens without the user knowing about it is scheduling. Scheduling works in FiveUp in two ways.
We at the beginning of every day take all of the users and separate them into three groups. And then each one of those groups get a different sending schedule. Now I did this. Because I didn't want like a bunch of five users to be at a table and all get five up users and they all get their messages at the same time because it's five o'clock and they're like, oh, their five phones vibrate and they're like, oh that's boring, that's our five of things Yeah, and then it like takes the magic out of it, right? Also, we like novelty as humans, so I was trying to really play up to that one. Anyway. That's what's going on. So if my code is working properly and I give it 10 users, when I run a divide users function that I've created, it should give me three groups back.
with two of the groups having three users and one having four users. So if we test that, what we'll do is make a handful of users Django is going to throw these in a test database and then toss them out when we're done, which is great. And then we're going to call the divide users function on those users with those users as an argument. argument and then at the bottom we'll assert how many should be in each group. And if we add up all of the users, they should it should equal the same number of users that are in the group. the database. If the tests pass, then we know that we're our code is working properly. And that
like we're kind of awesome. What now? Napping. Because all of that was really hard. And then we do more of the same stuff. We should think about what could go wrong and test for that. And we'll think about what can go right and test for that also. And with lots of kindness and generosity toward ourselves, because we will not always get it it right. We look through and try to test everything that we can test for. Dream it all up and see what we can do. Aim for 100% test coverage. And when you get there, you're done for forever. Unless you add any features at all or anything ever changes.
Um, but other than that, you're totally done forever. Yay for you Okay, let's go over what we've learned. First of all, we've got some good tools that are going to help us. Py test. PyTestCov, Selenium, and PyTest Django, all super awesome tools and resources. Second thing we learned is that using coverage can help reveal spots that you haven't touched. Tested and help you get your footing. Third, Selenium lets us impersonate a user quickly and lazily, which is the best way to do anything. Fourth, people use words like functional test and unit test in a variety of ways.
But the important thing to note is that we want our websites and our code. to work in the way that we as developers would expect them to work and also in the ways that our users would expect them to work. Also, Michelle Obama is amazing and she should totally be my best friend. The last thing that I want to say is that I wish someone had told me the real benefits of testing , which is Mental freedom and a safety net to know that I can try new things and I have this kind of tool that will keep me on track and bolster my confidence. And it's work. Like, I'm not gonna lie about that. And I didn't even get into mocking and fixtures, which are
genuine nightmare and I'm going to really get good at them later on. Um but it's really really worth it. So if you've been waiting for some kind of magical time Time to decide to learn testing. I really, really hope it's now. Thank you.
The talk recommends pytest, pytest-django, Selenium, and pytest-cov. Pytest provides the test runner, pytest-django integrates it with Django, Selenium simulates a user in a browser, and pytest-cov reports coverage.
Discussed at 5:37Run pytest with pytest-cov to produce a terminal coverage report, omit files that are already at 100% coverage, and list the exact lines that were not executed. This gives you a starting point for adding tests.
Discussed at 7:58A functional test checks whether the application does what the developer and user expect, such as visiting a page, clicking something, and seeing the intended result. Selenium can automate these browser interactions instead of requiring manual clicking through the site.
Discussed at 9:29Create a test user, open the browser, enter an email and password into the form, submit it, and assert that the resulting page contains the expected logged-in text, such as a greeting with the username.
Discussed at 13:25The talk uses a unit test to mean testing a small piece of behind-the-scenes code, such as a model or function, rather than testing the whole user-facing flow. The example tests a scheduling function that divides users into groups.
Discussed at 17:20Create a set of users in Django’s temporary test database, pass them to the division function, and assert that the expected number of groups and users per group are returned. For ten users in the example, there should be two groups of three and one group of four, totaling all ten users.
Discussed at 18:54Testing provides mental freedom and a safety net: it lets you try changes with more confidence while helping keep the code on track. It takes real work, but the protection and confidence are worth it.
Discussed at 21:15Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026