Transcript: Speed Up Your Django Tests - Adam Johnson
Hi, welcome to another episode of Django Chat, weekly podcast on the Django Web Framework.
I'm Carlton Gibson, joined as ever by Will Vinson.
Hello, Will, how are you?
I'm good.
Hi, Carlton.
Hello, Will.
And today we have Adam Johnson back with us, who's um uh well, longtime contributor.
My SQL expert came on the show before.
Just uh technical board member and just released a new book on um speeding up your Django tests.
So we're gonna talk about that.
Hello, Adam.
How are you?
Hello.
Hello from sunny London.
Sunny London.
Is it sunny today?
No.
No.
Well, it's evening, so what's the difference, right?
It's allowed to rain at night.
So you're you're on for a second time.
We'll try to focus the discussion on your new book on testing, since that's a topic that we can always talk more about.
Do you want to quickly mention the book and why you wrote it?
Sure.
So the book is Speed Up Your Django Tests.
Um the title is like it sounds quite niche, but I guess I've ended up writing quite a lot of other stuff in there, um, just like through the lens of like you've got tests, but you want them to be fast.
Um so it covers a load of like easy, easy-ish techniques to like flip some configuration or like run your tests in parallel that will make them faster, but then also dives into like what is a test, how can you write them to be fast, and um how can you write them to be accurate.
And we should mention your quick bona fides.
I mean, you've worked with Django for what over eight years.
You've worked on the Django testing library as well as a number of PyTest plugins.
Is that correct?
Exactly.
Um nearly eight years with Django, I think.
So you don't need to be unduly modest when you talk about talking about testing in Django.
Just wanted to get that out there.
Well, maybe just a quick question.
So so why performance?
At what point does performance become an issue?
Because for a lot of people, just having tests is an issue.
So it seems a little bit secondary to focus on performance.
And I know that you talk about testing in general through that lens, but what's when do people realize their tests are slow?
Like at what point in the process or the size of the code base does that happen for people?
Yeah, I think this is something I didn't touch upon much in the book.
I'm gonna add an update about like how fast should tests be.
Um I think like maybe one reason people don't write tests is because they're perceived as slow.
Um, normally more in like it's slow to write, like, oh, I don't know what I'm doing, it's a whole different paradigm of coding.
But also it can it can get quite tiresome to run this thing if it takes one minute, even each time.
That can be quite a noticeable difference to your flow.
But I know a lot of companies like they wait until their tests take half an hour before they even consider something.
Um, this is what happened to me uh in my previous position a few years ago at Y Plan.
Um, tests were taking 20 minutes, 25 minutes.
That's like a very slow feedback cycle to make a typo or realize that the future doesn't work in this situation and not get the feedback.
Can I just ask that?
So, okay, the whole test suite takes 20 minutes.
Okay, that's too long, but you don't run the whole test suite when you're writing some new code, right?
You you run one or two tests individually and they they only take you know a couple of seconds and then
that's part of it, Carlton.
Not everyone.
Yeah, I guess that's one thing, but yeah, and that's definitely something you should do.
Um but it's also like generally um pull requests are gated on the whole test suite parsing.
Yes.
So if you have like a bug in production, you don't want to be waiting an extra 25 minutes before you get that fix out.
It can easily become the thing that everyone skips even, like, oh, the tests are being slow, let's just merge the code, and then you just double your problems.
And it's Friday and it's lunchtime.
Ah, but
uh so in terms of writing tests, Python has the built-in unit test framework.
Django has its own test um was it that's based off that test.
I should know off the top of my head.
We call it Django testing framework.
It's not really
a nice name.
Uh but you know, you and basically everyone I know who works with Python or Django uses PyTest.
Could you just talk about why that is?
And I know you've noted that you know Flask also recommends PyTest.
Um how do you think of those stages of unit test to Django test to PyTest?
Well, I guess unit test I see as like something slightly historical in the Python context.
Um as Python was getting started, unit testing was also getting popularized through JUnit, which was this Java framework, and then its clones.
So unit test is effectively a port of the Java testing framework.
Um and that's why you might consider it quite unpythonic in the way it names things.
Like there's all these assert functions and they're all in camel case, and you have to put everything in classes, which is something that's not necessarily Pythonic.
Um we've got unit test, Django just built on top of what was there in Python.
Um, and there have been like a bunch of attempts to make a more Pythonic testing framework, and I think PyTest is the one that has stuck with the community, had a lot of effort put behind it.
Um so yeah, I think that's why we tend to stick with it.
That said, like when it comes to Django, like there's really a balance.
We've got all these testing features in the Django testing framework, um, like database control, like extra assertion functions, um, things like the setup test data, which I have like a whole section on because that's a real speed boost for your tests, that you can't easily reproduce in PyTest, or at least haven't been.
Um so I think you really have to mix them at this point to get the best of both worlds.
So you use like the Django test case classes um with PyTest.
I mean one the thing I really liked about PyTest when I first came across it was the plain asserts, right?
Because even now I have every time I'm like self-assert what?
If it's not assert equal, I'm like, oh assert raises methods which you use all the time.
I have to go and look it up because it's it's just I can't remember those methods.
But assert, yeah, yeah, it's fine.
Is it assert equal or assert equals?
That was even something that's like.
Yeah, I still have that, yeah.
I still have that addition.
Well, and I honestly off the top of my head, I can't recall which of the one is the one you're supposed to use now, except it's one of them, not the other.
Exactly.
It's assert equal.
And you just you get a warning if you use assert equals, but it still works.
You just you can add extra warnings to a test suite.
Yeah, I think I've stripped I think I've updated all three of my books because they've been brought to my attention that the you know past ones wasn't always like that.
Uh well, so I did want to um so speaking of these three different testing things, one thing I've seen uh with total beginners is if they're new to Python, they're new to Django, they're new to unit tests, they're new to Django tests, a lot of times, you know, everything is magic.
So where the fact that the Django testing things mimics unit test, uh often they don't know what's Python versus what's Django, let alone what's PyTest.
So I really am glad that you have your book to you know go further steps because I see people, I mean probably I myself too, have confused, you know, what is a Python thing versus what is a Django specific thing until I look it up.
Um so that I find it interesting that there's it's convenient and makes sense that it follows those maxims, but also means if you just kind of drop into a project or blindly follow along with some tutorials, you really don't know what's going on uh with these different layers of of testing things that are used in a modern Django stack.
Yeah.
It can be quite hard to figure out which piece of documentation I'm gonna go look at even.
Well, so so separate from you know the official docs and and your book, how how do you think about you know, so if so if I came to you with a Django project that, you know, maybe didn't have tests, how would you where would you start?
How would you structure that and how would you educate a client who maybe you know has a code base and their engineer quit or something and they hire you?
How do you do that?
Um well I guess with t testing it's always depends upon what the aim is.
Um I think we know in general, like you can test exhaustively, but it's not always worth it.
Um like in the hardware world, they draw the they have a much higher bar, they draw the line way further out, they will test things exhaustively.
Um every every CPU design will just be stress tested for days at a time.
Um, it's not something we tend to do in the web community because it's quite easy to push a fix.
Um but I like to try and keep a higher bar, I guess, than most projects and aim for something like 100% coverage.
Um but it really depends on what we're aiming to do.
Because if you've got a website that doesn't support much of importance, like if it's a gif sharing site for you and a few friends, then maybe you don't even need tests, you can just fix it when it has problems.
If it's like a voting website, then I hope it has like very good tests.
Something I'd ask you about coverage, um, because like there's one way to get 100% coverage quite easily, is if you test, do write big broad um broad integration tests that sort of I don't know, they they they test the the the total outside interface of the project, and by doing that, they call through every layer, and you can get quite close to 100% really quickly, but they're not good tests, right?
No, yeah, that's another thing.
I think the branch option A coverage like restricts you a bit more from doing that because that means that every if has to branch both ways, so it can be very hard from the outside of the project to run exhaustive tests before they become slow.
Um yeah, there's in coverage five, there's the new format and ability to build plugins that would like assert that this file is covered by that test file.
Um so hopefully we'll see something for Django where you know you've got your models file, that must be covered exact 100% in a test models file.
You've got your views file, it must be covered exactly by the test views.
Yeah, that that's a real helper, right?
Because well, it's easy to cheat.
And and not always a bad thing, right?
Like if you just have that one integration test, that's way better than nothing.
Hey, and like those tests, those tests work, right?
Like so you need to check, I don't know, your login flow.
Like that kind of high-level integration check that yeah, does this, does that, does that.
That's brilliant, and it'll catch things.
But um, for me, one of the advantages of one of the things I really why do I test the reason I really love testing is because it enables me to make changes.
I know I didn't break anything.
And if I if I've got small unit tests covering little bits of functionality, I can make those changes knowing that I didn't break didn't break anything in adjusting stuff.
And without that, ah, you can't do it.
Yeah, if you have those things that just test your login flow, you have no idea if this particular thing in the login view that you're tweaking is actually working or not.
So the book is on performance wins, but it in terms of talking about it, there's a number of kind of easy ones or things that you have whole chapters on.
What comes to mind if someone says, Well, Adam, what are some performance things I can do to improve my tests?
Um so like I have this easy wins chapter that is basically a bunch of configurational things to install.
Um some of them are quite context-dependent, um, but some of them apply to nearly every project.
So you've got like changing authorization password hashes.
That's something that's in the Django docs.
I just try and explain it a little bit better.
Basically, there like Django's auth module uses a deliberately slow password hasher.
So every time you check a password, which in tests will be every time you fake a login through the test client, um, it takes uh a few hundred milliseconds of processing power.
This this adds up.
So in tests you can swap to one that basically does nothing and is very, very insecure and should never be deployed in production, but it makes a test fast.
Well, sort of like the built-in uh server, among other things.
Exactly, yeah.
Um and then there's like a bunch of packages you could install or configuration to change because um Jenk has some built-in for in-memory um usage.
So like disk is still like 40 times slower than memory, even with a fast SSD, you're not gonna get a memory-level performance.
So just swapping your tests use in memory, um, that's normally a big win.
Uh and I guess the final thing that would be quite an easy thing to switch over to, especially when your product is young, is parallelizing your tests.
So this is built into Django's test framework, and there's a very good plugin for PyTest.
Um, but you know, your computer has many cores these days.
You should use all of them when running tests.
Um, it's no good just burning through one of them when you could be using 12.
I want to come back just a sec there because you call this like um easy, easy quick wins, and yeah, yeah, just just use it in memory.
But like that chapter's amazing, right?
Because you do you're like, oh well, you know, you can use a in-memory um uh think of an obvious one, files storage or something.
I don't know.
Okay, that's that's you know, that's not it.
But then you're like, oh, but you know, you could configure um MySQL and Postgres to use in memory, you know, in-memory file system, and then you know, you've got the in-memory performance that you get out of SQLite, but for you know, Postgres or MySQL, and you're like, oh wow, now that hang on, that's pretty cool down there.
Yeah, that was one I also was like, whoa, that I hadn't thought of that one.
Yeah, there's just like one line for Docker.
If you're using Docker for your database, you can tell it to mount the database on a temp FS.
And uh like I put it in the Django MySQL test suite, um, and it took 20% off the total runtime.
So and also for those playing uh Django chat you know, drinking game, Docker was mentioned.
So drink.
Docker, Docker, Docker.
Yeah.
Carlton, please.
I have a whole bunch of questions, but Carlton, I I tend to Okay,
so one you know, one thing that comes up with tests all the time is um, and this is slightly orthogonal to the topic of the book, but is um database steep, because that's you know, database is going to be one of the slowest aspects, but no matter what you do.
And Django, you know, very much tied to the database through the ORM.
So I mean what's the question now?
I guess it's really important to optimize there for your Django project because the nature of Django, the way you built you developed with Django, because we're model-based, because the ORM's there, optimizing that's important, I guess.
What would you say?
I mean well that also ties into migrations too, which I know you you have a whole thing about in the book.
Yeah, uh, let's tackle just like using the ORM first.
Um so so the ORM is basically a translator of objects to SQL and then calling the database with that SQL.
Um there have been a number of attempts to build like a mock ORM which keeps the objects in Python.
Um I think like this this is something that could work for like a very limited subset of the API, but SQL has so many oddities to it, um, especially the variant of SQL you're using.
Like there's so many differences between Postgres and SQLite, even, that I wouldn't want to test against a fake version of my database.
Um, it's just too much of a mismatch.
Um so I think it's really important there to focus on like how can we optimize the actual database we're connecting to.
Like in memory is one way.
Um, parallelization just uses multiple databases that can use multiple cores on your computer.
That's really a good way of doing it.
Um upgrading to the latest version, also generally you'll see a small or notable improvement in performance.
Um migrations are a bit of a different kind of fish because they're like this whole bunch of SQL and Python operations that you have to run at the start of tests to get a fresh database that your project expects.
Um there's not really like a way around that.
You have to get that database into the state that your program expects it.
Like as soon as you deploy your app to production, that's the state the database is gonna be in.
Um so there's like the keep db flag in Django's test runner and an equivalent in PyTest Django, that keeps the database around between test runs.
So if you're rewriting the same set of tests against the same thing, you can avoid that often massive overhead.
Um and the other thing is like squash your migrations regularly.
Um so combine like all these long sets of operations into one slimmer set.
Yeah, so you've in the book you've you've very followed the party line.
You know, you know, you can use the squash thing.
I mean, what's what what's your off-the-record um view on you know just sort of deleting all your migrations and clearing out the migration history table and starting again with one initial?
Um I've never actually done that.
Um I guess because the biggest Django project I worked on was at Wi-Plan and um it it didn't have Django migrations.
Oh.
Uh it did for third-party apps, because you can't get away from it for third-party apps, even like Django Contribut.
Um, but it kind of predated even South being good.
So there was a custom migration runner that just ran a bunch of SQL files in a directory.
Um it wasn't the easiest to work with.
Um, but I guess it was faster than Django migrations, especially at the time.
Uh as to like actually just deleting all your migrations and replacing them, uh, yeah, I think it works.
I think there could be even some documentation on how to do that.
Uh, because there's so many caveats.
Like if you're deleting run SQL operations, you're not gonna have a database in the same state.
Run Python operations.
I mean, but you you the key point is that all your databases need to be migrated to the exact same point, and you know, then okay, maybe, maybe.
Maybe.
Yeah.
I mean, maybe it's safer at that point to just take a schema dump of production and load that and then write new migrations on top of that.
But the other chapter I really liked was um about profiling, because I think profiling is kind of something that um we all know in theory we should do, but that you very rarely see a good guide to it.
And I think your chapter on profiling is just amazing.
It's like, yeah, this is this is here it is.
Here are the tools, here how they fit together.
Here's the UIs that you fill on top.
I'm like, yeah, that's that's almost perfect, Adam.
That could be a book in itself.
Oh, maybe I'll open it up.
Um yeah, profiling is something that has helped me spot a bunch of stuff in Django even.
Um, like uh so the two tools I really cover in the book are C Profile, which is built into Python, and PySpy, which is this um Rust-based profiler that basically knows how to read Python's memory layout.
It's quite magical.
It works with like Python 2.7 up to 3.8, maybe 3.9 now.
It can just read what is happening inside of Python, and it does this like a hundred times a second.
It pauses your program, checks what functions are running, records that, and keeps going.
And like I've been able to spot like, oh, this Django function is in the top 20 functions in my test suite.
It could definitely be cached inside of Django and things like that.
I think we should be doing more profiling in general as a community.
I've definitely had some like optimization pull requests made to my projects where I have a sense that this is in no way a bottleneck.
If you read the code, you might suspect it's a bottleneck, but if you did a profile, you might not even be able to spot it.
Yeah.
I mean that's one of the sort of golden rules, right?
Is don't don't guess where you're spending your time measuring.
I did I originally start that chapter with the old Donald Newf quote.
Premature optimization is the root of all evil.
Although I changed it when I was uh like rebalancing the agendas of the quotes.
Fair enough.
So I so actually, so caching we just mentioned you talk about that um in your continuous integration um chapter.
And I wanted to ask more broadly, so there's a lot of options for that, and I hear anecdotally almost everyone kind of uses something different for that.
Like what what is your preferred choice and kind of what is the landscape of good and bad options you've seen for for CI and Django projects?
Uh right.
So what CI should you be using?
Um I don't know.
That's something of a moral question alongside a technical question.
You have a greenfield project, it's all atom.
What are you gonna use?
Well, if there are repos on GitHub these days, I would use GitHub Actions.
Um I've heard of a number of people uh very much in favor of it.
Yeah, it it works great.
Um it's fast.
I mean, Microsoft have thrown a lot of money at getting a lot of runners there ready to run your open source projects or your closed source projects, um, a lot of community work.
Um but I moved to it because Travis CI was being slow, and then I discovered that um it's because GitHub introduced a new rate limit.
This was after I'd moved to GitHub Actions, but it's because GitHub introduced a rate limit on us on the way Travis was integrated legacy-wise.
So Travis just couldn't push the results back, and you needed to go and reintegrate your app with the new like GitHub apps rather than GitHub legacy integrations.
Um so maybe I wish I was still on Travis.
It had some benefits.
Yeah, because that was, I mean, Travis and Circle and Travis in particular was kind of the um had like a good default for open source packages.
So are you saying so does that is that something malicious on Microsoft's part or more just a change and they didn't decide to update it?
Uh I mean I guess I don't really have a read there.
Uh I never received a notification as a Travis user, and probably Travis could have detected that, but then I never received a notification on GitHub either, so GitHub could have detected that.
I mean, it could be all the above too, hard to know.
My little two cents on the same question.
It's like GitHub's GitHub vaccines is nice because it's right there and it's it's awesome.
But you know, whether you circle CI, that's great, yeah, that's fine.
Travis, quite good as well.
Been if you know, been using that for a long time with the open source stuff.
Use a hosted one, right?
Don't don't like unless you've got a real reason, don't spin up a Jenkins and try and manage that because that's hard work.
Okay, yeah.
That would be my one rule.
I like the last time I touched Jenkins, I was like, I'm never ever going to use Jenkins again.
It seems like it's the most common legacy thing, right?
Because it's free as in not developer time free, but free.
But I I see that the most.
I mean, I've used that, and when I I ask people about this, I often if they're not using a hosted one, they're you know, they're some version of Jenkins.
It's not that it it's not good as content, it's just it's just so much time keeping it up to date and running and in sync with what stuff you need, and yeah.
I remember the first time I came to a hosted CI and you know it was just like the scales falling from my eyes.
It was like this is the future.
I've been uh you know, I was like, we've got to do this.
I saw at the time I'd go around the company companies, go around clients and being like, yeah, we're gonna use hosted CI.
And that was like you know, my hammer of the year.
Yeah, like any time you save managing your CI environment is time you get for testing or feature developments.
I have another question about Carlton.
If get get yours in.
Okay, well, the other thing that's um like so the what I really like about the book is there's so many things that aren't testing related at all.
So we've talked about profiling, we're talking about CI now.
I mean ICA is testing, but you know, kind of also isn't, right?
It's not speeding up your unit tests, it's the whole development process.
But then there's a whole little section on cues.
Which I like you know, configuring your cues, and it's like you might be using this queue, and here's how you configure that, you know.
And it's like it's about
configuring for tests, but yes.
Yeah, I know, but it's like you know, it it's like a sort of mini essay on you know the different queue options that are available, and I'm like, yeah, super, and all my favorites are all in there, and it's like, yeah, that's so that's what that's kind of what I like.
I like like how you've gone over the entire spectrum of stuff, and it's not just you know, it's not just tests in the narrow sense.
Like I've read read a lot of books on unit testing.
It's like this the whole process.
So, anyway, that's not really a question.
It's just I I guess it's a bit discursive in that way, but I hope the titles let you like skim the bits that you don't need to know about right now and come back to them when you think you do.
Yeah, but it's not cra I mean it's anything but it's like a massively awesome resource.
It's only 160 pages, right?
So it's not like it's like this um distracting, but it's just massively dense.
And what else is in there that I really liked?
I um like advanced trip um tips on configuring the um test case subclasses and you know, using particular test case subclasses, and I yeah, I'm trying to think particular one.
Um but you know that's that's hard knowledge to gain.
Um so you know, thank you for putting it all down in the
thank you.
Well, I maybe this is the the segue I did want to ask at one point about um the writing process for you.
So I know that you've you've been writing almost weekly what for months now, and so the book kind of kind of came out of that.
Um could you just talk about upping your you know output as a blogger and then what the book process was like?
Because I think a lot of people think about potentially doing a book, but most people don't actually do it.
Right.
Um well I guess like it looks like I write every week, um, but I don't like I queue up unpublished posts, and then when it comes time to publish them, I look at the ones that like I'm like, oh this is this is nearly ready, and then I push it.
Um I guess that is one thing that I've learned is that like you don't need to be like perfectly ready to publish.
Um like take that MVP approach that we put in startups and put in writing because normally it's better that something is out there than it never gets out.
Um and you learn more by doing the whole cycle, not just by like writing one complete perfect post a month, but by writing like four different tutorials of different kinds and things like that.
Um, so the book came out of me like sitting down to write a blog post, which was like gonna be like top ten tips to speed up your Django test suite.
And like I wrote a bit of this, a bit of that, and then I had like a 4,000-word draft or something, and that wasn't even like in prose, that was just like bullet points and like do this thing, and like copied some text from elsewhere.
Um and I guess all that stuff was like already in my head and my notes from like years of working with Django tests, and I'd you know made drafts of like this could be a blog post on this one little thing, and I realized like just bringing them all together would actually be like more powerful.
Um yeah, I guess lockdown was really the biggest motivating factor for getting this done because just sitting at home all the time.
Only so much Netflix I can watch.
I'm gonna have to write my book now.
Yes.
Yeah, well, uh and kudos for writing it and publishing it.
It is, you know, it's kind of like until you're on the other side, it's easy to hear people say that, but then you realize it.
Though I will say, if you I don't know if you're planning to put it into paperback, but that's a whole another fun thing to deal with doing and updating.
That's maybe another future project, yeah.
Um I've got a bunch of updates to put in the book because like uh I published a library this week for mocking time because it was a bit of a yak shape.
I wanted a section on how to mock the current time, but I couldn't find a library that would be like perfect to recommend.
I knew of the trade-offs between two of them, so I wrote the third one.
Um so yeah, I I think the ebook form will be me for a little while whilst I like iterate and add a few more sections and maybe rearrange stuff.
Um, yeah.
I think because it's technical in, you know, I mean like uh to take examples of GitHub actions or whatever, that you know that that's liable to change.
And so to have it in the ebook format where you can update it.
And the good thing is like I have all these Django projects for the code that get included, so I can like upgrade my requirements to Django 3.1 in October, rerun all of those tests, although some of them are meant to fail, for examples, and then yeah.
It will definitely be an update process.
I don't know how often a new update will um
too often.
Um it's it can be very burnout inducing for me.
Uh at least once a month.
Um, you know, some version of something I did wrong, or Django changed, or Docker changed, or Python changed, or Heroku changed, or Stripe changed.
Uh it is important to do, it is also important not to jump on everything that comes in because I have it's an endless supply of stuff.
Um I'm I'm currently updating my Django for Professionals book because there's stuff around Stripe and then there actually, well, we could talk about this.
Um I asked on the Django forum, there's a thing around what is the thing I asked the question of.
You you helped you helped answer this, both of you.
Um give us a clue.
The thing that um one what oh one of the in the deployment checklists, something was added for 3.0 that was flipped, and then in 3.1, I think you did it, Adam, is gonna be switched again.
I forget which
Secure Referr.
So people noted for me that that had changed because in uh 2.2, the 2.2 version, there were nine things that if you just ran the deployment checklist failed, and then it's now seven, and then yes, secure referrer is one that uh changed, and then also the default will change.
So there currently what isn't a default, but then for 3.1 it's gonna be set to something else.
Anyways, it's stuff like that times a thousand.
Where it's educational, and I'm really glad people do it, but it's uh yeah.
So I I I I try to get better about devoting specific time to updates rather than just having it constantly weigh on me.
Yeah, I was not great.
I was reading um Harry Percival's book, Obey the Testing Goat, uh um doing this.
And he actually like deliberately pins a Django 111 and a specific Python version and says, use these throughout the book.
Uh I'm not gonna support the others.
If you see random errors, it's probably because of that.
Come back here and and install the versions I'm working with.
Um yeah, and and he'll update it at some point, but he's like very tightly integrated to specific versions.
I think that could work.
Well, you have to pin it.
I g I guess I would the two things I would say is one is beginners need it to be up to date or need and want it to be up to date, and and they will still install the latest version of Django even though you tell them not to, and they will get tripped up.
So for so maybe you know, Harry's book is a little more advanced, you can get away with it for a beginner book.
I think you you have to.
Uh the other thing is anything that's non-Django can and will change a million more times.
Like I mentioned the Stripe API.
Um, I would love to have nothing but Django and Python in my books.
I'm kind of getting there instead of waving my hands and saying, yeah, Heroku, yeah, yeah, Stripe.
Uh I think I'm probably actually gonna remove the Stripe chapter, for example, because it just changed, it's gonna change again.
It's more than I can handle.
Um I probably will keep up Heroku because I do want to show easy in quotes deployment.
Um, but anytime you step outside of Python Django, you're just opening yourself up.
Sengrid, Sengrid changed, you know, in April, all of a sudden changed the config.
So chapter 12 in my beginner's book was broken.
Um so that was fun.
You know, so uh you know, I I'm I'm hoping to I think either you have to charge a lot or just not do it to show integrations with non-core Django things, even if it's things that a modern Django stack probably uses.
As a content creator and updater, it's really, really hard to to do.
Well, one of the things I deliberately dropped from my book was uh well didn't put in was uh Docker.
There it is again.
Um Jeff Triplett fan posted about it.
There's like a new build kit integration in Docker, like this new way of building images, and it's like I don't know, a bazillion times faster.
I was thinking, okay, I'll put this, and then I was like, no, this is gonna change in like one month's time because they'll make it the default or something.
Yeah, yeah.
Well, I didn't even mention Docker in that list.
They, yeah, they there was some there was another bug like a month ago where people, you know, a dozen people are writing me and da-da-da-da.
And it's like, oh, I don't know, I gotta put on my thinking hat.
And there was something specific to Windows Docker, not Mac Docker, not Linux that was breaking.
Um, which, you know, the whole point of Docker is to not have to worry about systems.
Um and honestly, in my professionals book, which I know you you've gone through with with someone you were teaching, I've and I may well remove Docker from it in future editions, in part because it's just another lay of complexity, even though it is quite common.
But I don't know, it's a trade-off.
Because it you know, because these are modern tools, right?
Like, you know, PyTest, right?
PyTest is not Django.
I mean, it kind of is, but it really isn't.
Um your CI, uh, it's it's hard to strike that balance.
I think it's a little bit easier maybe in more advanced things, or it's maybe more easier for things that are you charge a lot for, but you to update stuff for you know, Docker and whomever, it's just makes people not update their stuff.
Yeah, with Docker I've I've tended now towards just using it for running the database and like Redis or Memcache and don't use an image for the Python stuff.
I feel like it's it's quite a bit easier just to have Python on your system with PyEnv or something, and you've got it there.
You know Python is working on your terminal.
Um you don't need to build images again and again and update for them.
In a team setting as well, that works out okay.
Um yeah, it has for like one client where I've put them in it.
What were you gonna say, Carlton?
Well, I I I'm kind of in the same sort of boat.
Like, you know, for for running, I don't know, some Postgres enabled you know database.
So I'm not installing that locally because it will destroy my entire system when I get it wrong.
Um to have to have you know various versions of Python set up locally and you know the the the core set of dynamic libraries I need to compile the the core dependencies, that's not difficult to do.
And this idea that you you don't need to have a known environment, you know, a reproducible containerized environment for that kind of local development.
Yeah, brilliant.
When you other cases, yes, totally.
But yeah, I think when you hit a certain size, there's definitely savings.
Like um, if you're using five different external libraries and you've got 20 team members, you're gonna have a real paint of a time getting all 100 of those installs in sync all the time.
So you one of the things you have that I want to ask about is this triple A test structure.
Could you explain what that is?
Sure, like I in no way invented this.
Um uh it came to me to a friend James Cook at his PyCon presentation one year, PyCon UK that is, um, and he's written a blog post on it, which I find myself pointing people to quite a lot.
Um I guess it's something that's implicit in testing, is that this is the general structure for how you write a test.
Um, but often tests like when they tend away from it, they become more difficult to use or run or less performant.
Um so the triple A stands for arrange, act, assert.
So you always think of your test as like arranging the environment.
So putting that pre-existing user in the database or calculating the URL to go and fetch or something like that.
Um the act is like the actually using the system under test, the component that you're trying to test, um, storing its result or results, um, and then the the assert is when you make some assertions on what happened, like did it actually update that user or did it um return the correct response?
Um sometimes you have multiple assertions, like did it both send an email and return the correct response from the view.
Um but yeah, I I tried to cover that in the book as a way of like here's how you should you think about tests and structuring them better.
What I liked about your discussion there was the way you um bought this brought into the well, you know, can I group asserts together?
Well, you know, think about it.
If you're arranging the same stuff and then you're doing the same actions, then you can make a set of related asserts.
There's no you don't have to duplicate that because that's obviously going to run slightly into time.
But you but you know, this is why I think there's so much more than just speeding up your tests in this book.
It's it's like, yeah, okay, you've you've sort of tied it back to speeding up your tests there, but it's general principles for how you arrange your unit tests or how you might think about the structure of your code.
Um as well.
I think you know, yeah.
There's that perhaps dogmatically applied idea of like a single assertion per test, and then you can end up with tests that are like one fetches a view and checks it returns a 200 status code, another fetches a view and checks that it has a certain header, another fetches a view again, and you've just tripled the work for like fetching the view.
Whereas you could really think of all of those asserts as like they're just asserting different parts of the HTTP response, which is ultimately like a text file.
One one thing I'd like to do in that um circumstance where I combine assertions is I'd like to be able to sort of conditionally assert, you know, even if that one fails, run the other assertions, tell me what else went wrong.
But it the tests stop at the first one that fails, and it's a bit like ah, well but what but you know, the actual informative one is four down sometimes.
What do you think about that kind of scenario?
Is there a is there a magic key there to
there are there are different attempts to solve this.
Um yeah, like maybe if it's something like an HTTP response, you could imagine some kind of magic object that you say, you know, say so code should be this, header, this should be there, this should be in the body.
You compare that object with the actual response and it figures out all the differences, then it displays an error if there are any.
But more generally, there's like a tool in unit test and therefore Django's test framework called subtest.
Um, and this is for like a context manager where you can mark a block of code as like this is like part of this test.
If it fails, still continue and then capture and show all of the errors at the end.
Um, and then in PyTest, there's PyTest support for subtest, or there's a plugin called I think PyTest check, where you have all these check functions that do the same.
Um, I don't think there's like a perfect solution out of all of these options right now.
Subtest, if you want to use it per assert, that's like doubling up your typing and making your test much less verbose.
Um I guess it would be really nice if just you could have PyTest like automatically keep running through all the assert statements.
It's not always possible though.
Sometimes you have an assert statement that does another action implicitly.
And that's part of using the AAA pattern.
If you have your act like right there in the middle before you do any asserts, then it's gonna be easier to make all those asserts.
Yeah, because in Django's test we use subtest a lot for um parameterization, you know, where you've got four or five different versions of the same test, and you know, PyTest does it differently with um parameterized fixtures, but you know, it we use it a lot there for that.
But I like the idea of using it for keeping your your tests running.
I hadn't really thought that.
Well, as ever, I mean, so we're around 40-minute mark, but there's two questions I definitely want to get in.
Um, and I don't know how you have Carlton.
I want to ask you about mocking, and then I'd I'd like to ask you about the Django technical board and who is on that and what that entails, because it's important, but I'm not sure how aware people are.
So um either one of those two.
I mean, in particular, you have a thing called the danger with mocking, which sounds sort of exciting.
Okay.
Um was that the question?
This isn't like some pop that will be cut out of the podcast.
Speaking out loud, either so I wanted to ask about the danger of mocking, and then also both of you are on the technical board.
Or the technical well, well let's talk about that.
Yeah, um, so the technical board is um five of us who have been elected to manage Jenko's technical direction.
Um, this doesn't mean we're involved in every single decision, but the idea is that for bigger decisions, we are the voting body that can say yay or nay to any particular suggestion.
Um with the recent implementation of DEP 10, we're meant to be taking a more active role as well.
So we'll start seeking um projects and like evaluating them um together as a board.
Um and I know Colton even made a GitHub team for us today so that we can help review Marius' PRs whilst Carden is on holiday.
Um yeah, so the five of us are myself, Andrew Godwin, Emmerich Augustin, Marcus Holterman, and Simon Charett.
Um yeah, does that answer the question?
Yeah, I think so.
Just I I think it's you know it's a very important board team that I know that not many people uh are necessarily aware of.
Um so we've had Andrew on the show.
We're actually interviewing Aimerick tomorrow.
Um we've had Marcus on and we hope to have Simon on.
Um but you know when I talk to Carlton about who's really with their sleeves up doing things in Django, uh all five of you are I mean the Mount you know not what is it?
Mount not Zeus.
Anyways.
Mount Olympus.
Mount Olympus, thank you.
On the Mount on the Mount Olympus of of active Django things.
I have no idea which uh god or demigod I'd be then.
Probably not a very important one.
Um yeah.
Like everyone on the technical board, yeah.
I'm impressed with all my fellow technical board members' like contributions.
I think the there's two things that I'd say is one that to be on the technical board you you have to be active, like you have to have a shown history.
Uh to even be a candidate to be you know on the technical board, you have to have a shown recent history of active contributions.
Um so that's one thing.
The second thing is that the technical board, whilst they're there, is this decision-making boxes.
Normally, most decisions are are reached by sort of consensus in the discussion on Django developers.
You know, not often, I mean, recently there was a um a discussion about um adding type ins to Django, and there there was there's been lots of discussion over several years, and you know, lots of proposals and lots of pros and lots of cons.
And at that point there wasn't a consensus that came out, and so you know, we asked the technical board to you know make that decision.
But that's quite rare.
Normally, you know, there's a bit of debate, a bit of conversation, but people are like, yeah, no, actually, that's the way forward, um, which is kind of nice, I think.
Yes, I think we work pretty well as a community, and it's good that we aren't needed to make many big decisions.
Good, thank you.
Yeah, I mean we'll link to there, there's a team section on the docs, um, and it is with depth 10.
There'll be a little bit of changes, but um so mocking.
What is mocking and what makes it dangerous?
The danger with mocking, yes.
Um yeah, I I guess there's just generally a trend to overuse mocking in the Python world because it's so easy.
Like Python as a language lets you do it well, and then unit test.mock is like um ready in there to go and it can just mock pretty much anything.
Um so I called my mocking chapter targeted mocking because I think this is really the key point with mocking is that you should make them as targeted as possible and know what role they're supposed to be fulfilling when you make them.
Whether it's there just to check that your system calls another system or it's there to stop you from calling the other system.
Um, and then like making them really targeted is is the is the key.
If you use a unit test.mock object, it's like very untargeted by default.
It's just this weird magical Python object that you can call any function on it, and it's there and it returns another mock, and then that you can compare with anything, and like uh I think for beginners it's very hard to get your head around Python even allowing you to do this.
Like, there's an object that compares equal to every single number, every single string, every single dictionary, um, no type errors ever.
Uh, it can really like break your program, but in tests it tends to just like pass every test because it just kind of allows everything to happen without actually testing anything at the end of the day.
Also, so I see people or projects where there's been mocking and it's mocked out something, but what you end up the unit tests are testing the mocking, not the not the system.
And it's like, but this why is why did it break in production?
And you've got all these unit tests.
It's like uh yeah, but they in the end they're not asserting anything.
Yeah, right.
You draw a boundary, you put mocks at that boundary for the other system, and then actually what you tested against this mock is completely different.
So again, it's like the mock should be targeted, it should be known to be like a good replacement for the other system.
Um so like one of the libraries I mentioned is requests mock.
This is a good mock for the Python requests library.
It it actually injects itself inside part of requests with a known public API.
Um so like quite a lot of the actual requests code gets tested when you run this.
It's just not the third-party HTTP requests.
Um yeah.
Rather than like mocking out with unit test.mock the entire interface, and you didn't even check for typos with like request.get.
Yeah, did you pass the right did you call the right method now?
Yeah.
Thank you.
Carlton, I don't know if you had any other things you wanted to ask, Adam.
No, I mean uh no, I mean I was I was reading the book last night, I was just you know blown away.
I was just like, what a really good, you know, really good thing.
It's not for novices, but it's you know
it's a much if you're a Django developer, I think.
You you you you you use Django, you write unitests, it's like just get it.
Even if you don't write unit tests, there's loads of other stuff in it.
It's really good.
Well, I hope it convinces you to write unit tests.
Do you personally, Adam?
I mean, you uh have any projects or um things you want to call out that you've been working on?
Well, there's the book, there's and this time machine library I mentioned earlier that I released this week.
That's that's actually my first Python library using C under the hood.
Um, and I'd like to see it tested a bit more.
It's like a somewhat of a drop-in replacement for a popular freeze gun.
So if you're using that and you're listening, then maybe try it out.
Um other than that, just like follow my blog.
But I guess it's not hard to come across it if you're reading things like Django News.
So yeah, I mean pretty much every week it's just like, well, there's a couple from Adam, and you know, it's yeah, along with uh this podcast, it's one of the weekly things.
So good.
Well, thank you so much for taking the time.
Uh thank you for writing the book.
Um, I hope more people will read it.
I know many people have bought it, but again, I think it's a core part of the educational canon of Django stuff.
So thank you.
Thanks for coming on, Ed.
Thank you very much for having me again.
Um brilliant to chat.
Okay, everyone.
We are at Chat Django on Twitter, and we'll see you next time.
Bye bye.
Join us next time.
Bye bye.