DjangoCon US 2017 – Practical Unit Testing in Django by Wayne Merry

(happy music) – Thank you for coming Can you all hear me? No fall back here

Today we're going to look at Practical Unit Testing in Django If you went to the testing talk yesterday, we're going to dive into it in more detail specifically about unit testing, rather than general Django testing If you've not done mocking before, a good intro talk is from DjangoConUS 2015, Why Unit Testing Doesn't Have To Be So Hard That's when my introduction to mocking happened then, and then using that in a Django context in a few teas later, then we've developed some techniques That's definitely a good talk to check out if you're new to this area

What we're covering is we're covering unit testing We're going to look at some examples of testing with views, forms, fields, and some template tags We're not covering integration testing, so I'm not going to get into the whether you should use or not use mocks when it comes to those things that much, apart from stating a personal opinion I have never used mocks in integration tests, if I can avoid it anyway But they're essential when it comes to unit tests

Why unit test I started my Django adventure by doing the tutorial The tutorial has a section on testing That section on testing is integration testing Now, the problem is, as a project grows in scope, it becomes very hard to get a high level of code coverage using the Django test client, where the code that you're trying to test might be 20 method calls down the tree, and so that doesn't really work in terms of really exploring the code that you're posting on a test in a particular module

We need a more direct way of attacking what we're trying to test rather than trying to test the whole stack In a project, you would do integration testing, so those tests are still there But then you, so there's a percentage of tests that are integration and functional tests, but a whole lot of tests are going to be unit tests There's many dependencies that can exist in a project Methods can often be more than just take what the parameters that come in and a result that comes out, there's side effects, there's classes, there's attributes of classes

You might be changing those attributes of classes, and so those things are side effects, and they increase the complexity of testing We need a way of getting some sanity about all this, because that kind of drives you crazy Sometime, really, it can be so complicated, it can just not be even plausible to try and manage it at such a high level What is a unit test? The idea is to place one method under test You're testing the internal operation of that method, you're testing how it calls other methods, and you're looking at what it does when it gets the results of other methods back and what it might do with that, which might be simply just returning it onwards

Then you presume everything else is reliable Python's duck based typing So can you tell me a bit of interaction here, what is that animal that we see on the screen? Oh, we don't see anything on the screen at all now Hopefully, that stays there, touch anything OK, the point is, it doesn't really matter what is the big picture of what you're testing

You are testing a method We've got the nose of a duck, a little toy duck on this lion It doesn't matter the thing is a lion, you're just testing that narrow bit with that red nose So long as that does, you only need to do enough to make it work in the context of your method Everything else that it does doesn't really matter

I'll fill that out in some details as we go along But to get you thinking, if you're testing some code that takes an ORM result, and you're, say, adding something to it, you actually don't need the ORM to test what you are doing The ORM itself is out of scope of your particular method We'll have a look at some examples The point is, if you're testing just at that red nose, that's all that matters

Everything else is irrelevant Our first sort of domain that we want to talk about is testing class based views If you're using functional views, I don't really use them anymore, so I'm going to be focused on class based views Now, class based views, there's a big hierarchy and there's lots of methods in there What then typically happens is you might be inheriting from, say, create view or update view, and you might be defining a few methods that add to that, just do a few things extra, maybe just one line of code extra

What you're testing is that one line of code and the call to the super function, and the result that comes back That's all you're testing You're not testing anything else As this slide talks about, each override that you make is going to be a method that you're either deriving from the class and writing a specific override, or perhaps you've noticed you've done that a few times, and so now it's time for a mixin, and so we're going to define a mixin If there's one or two things in one or two methods and we want to test that

What we want to do is we want to test those methods Now, what we don't want to do is we don't want to call the Django test client, because that's an integration test, and that relies on the whole kit and caboodle to be there, and we don't really want that What we need to do is we need to basically create enough of our view in memory so we can call the function we actually want to test, and so the top function there is a function that just rips out a little bit from the as view function that's from the Django class based view hierarchy to set up a test object, a view object, I should say It sets up the attributes for request args and keywords, and returns the view If you look at as view in the Django source code, you'll notice that that exists in there it does a few other things, but they don't really need it for our unit test

Then to actually test this thing, in the setup function in the test class, we see that we go to the RequestFactory, that was the existence of the RequestFactory we talked about yesterday We, in this case, we're testing a get method, but that could be other HTTP methods It doesn't really matter what URL you'd give it, you don't need to worry about URL resolving, you can call it abc, right? It's really irrelevant, right Just feed it some string We create our view, which is the view under test, and we call that method that's at the top

That sets up the view, and then what we can do is we can directly call the method If our method, if our class on a test has got, say, a dispatchment that override, then we can just directly call that by going viewdispatch with the request and any arguments, keyword items We could directly call that method, we don't need to worry about everything else that goes with it If we have common functions to override is get context data

You just call it directly with a dictionary as your keyword args, then you look at what it does inside Moving on to forms We want to test, the idea is to test every action of a method, to get some kind of assurance that this code is going to do what it does Here's a function we want to test It's got, so it's the clean method of a form, and what it does is it's testing two dates, and if the end date is after the start date, we just want to make it the same, like we want to make the start date the same as the end date, so the start date's later

Does it do that or not? If there's an exception, then we want it to basically do nothing Here are two tests that do that If we have a look what they do, the first thing is they patch the clean method that's inherited, all right, the super call is going to call the form clean method We actually don't want to call the form clean method, because we're not interested in testing what that does We just want, so we need to patch that

Autospec means it'll test whether we're calling it in a way that would be expected, and then we give it a result value We're just going to give it a result of abc Call it whatever you want So then the method, the first line is it creates the form, form = selfTestForm

Then, it sets up some clean data Now, when you were doing an integration test, that's already done somewhere else, but we're just going to be calling clean, so clean data doesn't exist, so we need to make that That's a dependency We need to set that up, and we'll put some clean data in there In the first one, there is no end date, so that would throw an exception

We're testing for only a start date It says result = formclean It calls the clean method directly Then it looks at what comes out

The first assertion is, did it call the super method? Did that clean method of the form get called? Was it called once? Which is what we expect The previous method has got one call to super What happens if we made a coding error and we called it twice? Well then the assertion will fail Then the next line is it asserts that clean data has only got a start date in it There's no end date that appeared, because our code's not supposed to do that

Now, if I wanted to pass it, if I wanted to throw the exception and not capture it, I could use an assertion called assertRaises, and put the exception in there, and then the test will pass if an exception is thrown that's not captured I can, whether I need to throw the exception or not, I can test specifically for that Then the final thing is, this last assertion is the result abc We go back and have a look at the code, it takes the result from super is put into a variable called result and then down the bottom, return result If I leave that bottom line out, forget to return the result, then that final assertion will fail

I am testing, it's my method that takes the result of the super call, and will pass that out to the caller I want to test that, that's something that my method is doing, so anything that my method is doing, I want to test that That's what that final assertion, assert result abc needs to test If I change the return value back in the patch at the top line to def, then my assertion will file on this side test for def That's the concept of detailed unit testing

Going on, it's just talking about this at a more conceptual level Methods do things, and we want to try and capture each thing that it does Some of it is direct in method action, such as assignments or doing some sort of mathematics or whatever The other thing that they typically do is call stuff Most often, but not always, that's a call to super

Sometimes it's a call to other things Whatever it calls, you want to test that it's calling it as you expect it will call it given the inputs that you're fitting to this function Say, if you only sometimes call super, based on certain inputs, then you'll want test case that test for the domain of inputs that should call super and then for the ones that shouldn't call super, you'll be looking that its call count is zero Now, as soon as you introduce an if statement, you've got a code branch, and so that's likely to mean more than one test case so you can test each branch If you're getting too many of them and you're getting a whole pile of test cases, that's a prime indication that you need to break the thing up

Just a few notes on, in the Python mock library it has this thing called call_args and call_args_list, so to save you a bit of blood, sweat, and tears, the first index element contains all the positional arguments It's not just stuff that you're putting in *args, but anything that's called as a positional argument The second index is anything that you call as a keyword args If the thing is a method that's on an object, self will be the first positional argument But if it is a class method, class is not passed as a positional argument even though you put it in the code, because Python already knows the name of the class, so the class is actually not passed as the first positional keyword arg, so the first positional arg, even though you actually physically write cls or whatever you're going to call it in the code, and if it's a static method where you're expecting nothing to be passed at that stage

Call_args_list is used when you're calling the function more than once It allows you to make assertions on the parameters for each call Next thing is mixins We discussed with mixins a little bit before If you write a mixin, and you'll be wanting to test its method

In this case, we've got a validation object which is designed, it's a mixin, so it's designed to work with other stuff It has an init method that does some stuff To test that, in our Test_ServerValMixin, which is not in your database, so we can use SimpleTestCase to make things a bit faster We create a dummy field that inherits from the test class, the class that's under test, plus CharField That field doesn't need to do it, that class doesn't need to do anything else, so that's water about passed

When we create this field, we're going to be calling init method But we don't want to call the init method of CharField Because I don't care what goes on inside that, I'm not testing Django, I'm testing my code I don't want to, I just, I need to patch that thing Wherever the top, where the ServerValMixin is defined, that's the thing I put next to patch

object, ModuleWhereImportedTo, not where it's defined, where it's imported to, the init method autospec = True, and it doesn't return anything, so I don't any return that Then in the test, I create the test field, you can see I'm using keyword args, the double star, I'm passing in whatever, it doesn't matter what, I'm just passing in some dictionary But this init method is supposed to pass that on This test case is testing where I provide no settings to the actual mixin But the mixin is used in the context of where it's mixed in with something else

Never use it alone One of the assertions, which is the bottom one, tests to see whether the init method of the base class was called with that 11 and 12 dictionary That's what that bottom line is doing I'm actually seeing, I'm testing the super call in the init method If it doesn't pass it on, then my assertion will fail

Just a few comments about mocks By default, when you create a mock, everything's defined If you're testing that it's not there, you need to delete it, which is under, so in this test case here, we see on line three and four this del mock1save and del mock2save

This is testing something that is a mixin that could be used with a model form, but it could be used in an ordinary form Model forms have got a save method and ordinary forms don't Well, what happens, I want to test it with something that has it, and something that doesn't Is it going to work or is it going to throw an exception? This test method tests for that It's setting up a view that's got some forms

It creates a dictionary of forms, form1 and form2, and it then passes that to the form_valid method of the thing that's on the test Another note is, at the top you see there's three patches It's patching the get_success URL because that's what form_valid, if you in your call form_valid calls the get success URL, but I don't want to have to do that I don't want it go through the class based view by run I just want it to do what I need this thing to do

I don't want it to redirect It's calling, it's saving several forms, it's doing it in transaction Is that transaction actually invoked, and the assertion that tests for that is the fourth that from the bottom One little quick tip when it comes to these patches, when there's more than one of them, the order you need, it's like a mirror image, the closest one in is the first, the TA patch, then the next one up is the HOR patch, you need to do them in a mirror order, not the top patch is the first positional argument after self, you do them in like a mirror image is the best way I've found to think about that We're getting low on time, so just a quick look at template unit testing

Here's a dictionary object that, the base test just does what Django does, but it also does indirect lookups I tested to see whether it can do those things or not All I need to do is setup a template with a minimal string that just loads the test tags and then actually invokes the tag, setup a context, run it, which is what that render function does, dumps the result in rendered, and then I can assert that that rendered, the tag is doing what it's supposed to be doing All right, so in summary, a unit test is focused on a specific method, and that's it Whatever that method's doing directly, or what it's calling

When I say what it's calling, only how it calls it and what comes back, not what's going on inside, that's effectively a black box View classes, we set up the instance with sort of a dummy function and you'll probably find versions of that around on the Net, or I'll look to try and put those slides up, they've got the Twitter handle there, so I've got to get them hosted somewhere You're testing on arguments to test the cohesion, and you use dummy classes, subclasses to test mixin Mocks, they have attributes, which we didn't get too much time to go into You need to delete attributes and methods where you need to test for their absence

Have you got any questions? (applause) – [Audience Member] Do you find that getting in, particularly the last example where you're sort of mocking things and getting into a little bit of the semantics of how the underlying layers work, do you find that tests, the semantics change between versions of Django or versions of Python? In other words, your tests are very detailed and very specific, and do you find that there's an additional maintenance burden by getting into that much detail that you're doing? I mean, the tests look very nice, but I worry about maintenance – OK, it does exist, but it's minimal The advantage of testing this way is that it pays off for it so many times What typically happens as you write all these unit tests is, there's a lot of assurance on the underlying code When you come to do an integration test, you might find that a few bits don't quite fit in quite right

But once you fix the cohesion issues, everything just seems to work Going from one version of Django to the other, the problems that can arise from that, where you've actually got to change the code under test, not the testing code Because the testing code is mocking out all this other stuff, but your code is expecting Django to work in a certain way, and that's all you're testing The problems are really, when they do arise, with your tested code, not your testing code, and you want your test to fail in that situation – [Audience Member] I have a question about, how do you go about deciding if an integration test would be better for a particular view rather than doing a unit test for each little block of code? Because it seems like with an integration test, you can easily request, do the request, and make sure you get back what you're wanting

But with a unit test, in your examples, anyway, there's a lot of setup in terms of mocking the things that you don't want, and you have to decide what you don't want and that type of thing? – Well, the answer to that is, I do both I integration test the user stories, including the alternative cases for use case text I want to see that those stories are captured in the integration tests Then the unit tests focus on the detail of each function, so I don't do a trade off there, I actually do both It improves the integrity of the code

Once you get used to these mocks and dummy objects, you get used to pumping them out pretty quickly like the ORM, you can have complicated queries, you don't need to worry about setting up fixtures, you just look at your parameters An integration test needs fixtures and the integration test will see if your query makes sense or not, and then your unit test can deal with all the little edge cases where it'd be pretty much impossible to create all those fixtures In the end, it ends up working quite quickly – [Host] Thank you, again, Wayne (applause) (sweeping music)