We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Practical Unit Testing in Django

00:00

Formal Metadata

Title
Practical Unit Testing in Django
Subtitle
Mocking and Testing Techniques
Title of Series
Part Number
39
Number of Parts
48
Author
Contributors
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
This talk is an opportunity for you to explore practical ways of improving the test code you write. Unit testing can be challenging, but with the right toolbox of techniques, it is much easier to write unit tests that not only enable high degrees of code coverage, but assurance on each action of your code. Django provides an excellent test environment that facilitates testing across the whole of a project, however Django’s documentation and many online examples focus on integration tests. Any typical use of the Django test client is an integration test. Tools such as selenium also provide a frame work for usability tests, functional tests or integration tests. What is missing in this is a close look at unit tests. It is difficult to obtain high code coverage with integration tests alone. This talk will build on Daniel Davis’ DjangoCon2015 talk “Why Unit Testing Doesn’t Have To Be So Hard”. That talk introduced the concept of using mocking to deal with the complexity of unit testing and gave a number of simple examples. In this talk, we will apply mocking, dummy objects and harnesses to unit test in the Django environment. We will focus first on class based views. Django provides an extensive Generic Class Base View hierarchy of base classes and mixins. These define a series of methods that focus on various elements of the response process. For more complex applications, this system provides much of what is needed but often customizations are needed and these can take the form of subclasses overriding one or more methods, or perhaps mixins that are built to implement abstractions of these customizations. In order to unit test these customizations, we want to place each individual method under test. To obtain strong assurance of code performance, we want to place under test each action of the code, plus its coupling with its base class(es). A test harness, mocks and dummy objects all assist in this process and we will explore examples of such. Mocks particularly facilitate our tests by us being able assert on what is passed on other method calls and on the super() call. Mixins are used to implement customization abstractions. Their methods can be unit tested making use of dummy subclasses. Form classes also benefit from unit testing. Form classes may define clean methods for validation, and these clean methods can be called directly in unit tests for both valid and invalid data. Some modelform classes may implement business logic in their save() methods and these also highly benefit from unit testing. Both forms and views often make use of the ORM. When performing integration testing, this often means setting up test fixtures, but for unit testing it might be much more efficient to mock out ORM calls such as filter(), all(), count(), etc. Sometimes code under test will chain these ORM functions and this also can be mocked. We will then consider a more complex example of a view that makes use of an inlineformset. inlineformsets are more complex form objects, but various approaches can be used to unit test views that make use of formsets (along with unit tests of the formset itself). We will close with some template unit testing. The content of this talk is built on examples taken from real systems implementation. These should give many Django practitioners a boost in their day to day testing toolkit.
6
Thumbnail
42:19
Execution unitSoftware testingNP-hardCovering spaceField (computer science)DisintegrationCodeLevel (video gaming)Component-based software engineeringMiddlewareEndliche ModelltheorieClient (computing)Military operationSound effectComplexity classLatent heatPoint (geometry)Function (mathematics)Dependent and independent variablesInstance (computer science)Touch typingObservational studyComa BerenicesPoint (geometry)Software testingInsertion lossCodeComplexity classData compressionPhysical lawExecution unitFactory (trading post)ResultantContext awarenessINTEGRALDivisorSign (mathematics)DistanceDifferent (Kate Ryan album)CASE <Informatik>Commitment schemeTesselationGenderGoodness of fitUnit testingParameter (computer programming)Quicksort1 (number)Level (video gaming)Mixed realityData managementFunction (mathematics)TouchscreenInteractive televisionObject (grammar)Functional (mathematics)BitNetwork topologyPlastikkarteCausalityTerm (mathematics)Line (geometry)Client (computing)SoftwareProjective planeField (computer science)Slide ruleCategory of beingEvent horizonMultiplication signGroup actionLinear regressionAdventure gameOperator (mathematics)Computational complexity theorySheaf (mathematics)Right angleSystem callInheritance (object-oriented programming)Multitier architectureUniform resource locatorString (computer science)Order of magnitudeSemiconductor memorySpacetimeDomain nameCoefficient of determinationAttribute grammarExistenceStack (abstract data type)Module (mathematics)Computer animation
Equals signException handlingSoftware testingGroup actionFuzzy logicComplex (psychology)Condition numberCodeMetric systemMachine codeSuite (music)Object (grammar)Complexity classParameter (computer programming)Group actionSystem callCountingPatch (Unix)Subject indexingSoftware testingResultantUnit testingPosition operatorElement (mathematics)DatabaseParameter (computer programming)Server (computing)CodeCASE <Informatik>Line (geometry)BitObject (grammar)Field (computer science)Complexity classWordLevel (video gaming)INTEGRALException handlingBranch (computer science)Functional (mathematics)Equaliser (mathematics)Price indexLibrary (computing)Context awarenessElectronic mailing listArmValidity (statistics)Module (mathematics)Domain nameoutputStatement (computer science)1 (number)Key (cryptography)QuicksortError messageEntropiecodierungMathematicsMessage passingGreatest elementLoginArchaeological field surveyParadoxSet (mathematics)Motion captureProcedural programmingGenderOrder (biology)Mixed realityExecution unitDecision theoryPlotterType theoryObservational studySign (mathematics)PlastikkarteHypermediaComputer animation
Default (computer science)Database transactionSoftware testingExecution unitTemplate (C++)Digital filterKey (cryptography)Data dictionaryTask (computing)Complexity classInstance (computer science)Cohesion (computer science)Inheritance (object-oriented programming)Object (grammar)CodeRevision controlSemantics (computer science)Software maintenanceSoftware testingAdditionQuery languageINTEGRALCASE <Informatik>Optical disc driveField (computer science)Tracing (software)Sparse matrixExecution unitWordContext awarenessObject (grammar)Default (computer science)Line (geometry)Greatest elementOrder (biology)Database transactionParameter (computer programming)GenderMultiplication signFunctional (mathematics)Template (C++)String (computer science)Goodness of fitComplexity classQuicksortWater vaporBlock (periodic table)Computer fileTerm (mathematics)System callLatent heatStatisticsInstance (computer science)Type theoryAttribute grammarUnit testingBitExterior algebraCausalityIntegral elementPatch (Unix)Mixed realityUniqueness quantificationMedical imagingGroup actionProcess (computing)Video gameInheritance (object-oriented programming)System on a chipMessage passingChemical equationEndliche ModelltheorieCivil engineeringUniform resource locatorValidity (statistics)VolumenvisualisierungResultantPosition operatorCohesion (computer science)Focus (optics)Set (mathematics)Black boxSlide ruleException handlingData dictionaryTwitterComputer animation
XML
Transcript: English(auto-generated)
Can you all hear me? Alright, no phone back here. So 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 a bit more detail specifically about unit testing rather than general Django testing. So this if you're if you've not done
mocking before, a good intro talk is from DjangoCon US 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 and a few tiers later then we've sort of developed some techniques. So that's definitely a good
talk to check out if you're new to this area. And so 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'd never use mocks in integration tests. I can avoid it anyway but they're essential when it comes to unit tests. So why unit test? I started my Django adventure by doing the tutorial and 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 placing under test in a particular module. So we need we need a more direct way of attacking what we're trying to test rather than trying
to test the whole stack. Like in a project you would do integration testing so those tests are still there but then you 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. And so we need a way of getting
some sanity about all this because that kind of drives you crazy. And sometimes really can be so complicated it can just not be even plausible to try and manage it at such a high level. So what is a unit test? The idea is to place one method under test and 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. So Python's duck place 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. Don't touch anything. Okay so the point is it
doesn't really matter what is what is the big picture of what you're testing you are testing a method. So we've got the nose of a duck, a little toy duck on this line. It doesn't matter the thing is a line you're just
testing that narrow bit with a node that red nose and 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 and I'll fill that out in some details as we go along but to get you thinking if you're testing some
some code that takes an OOM result and you're say adding something to it you actually don't need the OOM to test your what you are doing. The OOM itself is out of scope of your particular method and we'll have 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. So our first 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 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 and that's all you're testing. You're not testing anything else. So what we saw is the slide talks about each override that you make is going to be a method that you that you're 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 mix-in and so we're going to we're going to define a mix-in that does one or two things in one or two methods and we want to test that. So what we want to do is we want to test those methods. Now what we want 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 kicking a bootle to be there and we don't really want that. So 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 of from the as view function that's from the Django class space view hierarchy to set up a test object a view object I should say so it sets up the attributes for request dogs and keywords and it 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're not really needed for our unit test and then to actually test this thing in the setup function in the test class we see that we go to the request factory that was the existence of the request factories talked about yesterday we in this case we're testing a get method but that could be that could be other HTTP
methods it doesn't really matter what URL you 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 right we create our view which is the view under test and we call that method that's up the top and that sets up the
view and then what we can do is we can directly call the method so if we if our method that if our class on a test has got say a dispatch method override then we can just directly call that by going view dot dispatch with the request and any arguments keyword arguments so we can directly call that
method and we don't need to worry about all everything else that goes with it if we're common functions to override is get context data you just call it directly with a with a dictionary as your keyword arms and then you look at then you look at what it does inside so moving on to forms so 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 so here's a minute here's a function we want to test so 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 at the end date so if the start dates later so so does it do that or not and if there's an exception then we want that not we wanted to basically do nothing so here are two tests that do that so we
have a look what they do the first thing is they patch the clean method that's in that's inherited all right the super call is going to call the form clean method we actually don't want to call the the form clean method because we're not interested in testing what that does we just want we so we
need to patch that and so your respect means it'll test whether we're calling it in the way that it would be expected and then we give it a result value we're just going to give it a result of ABC and call it whatever we want and then so then the method the first line is it creates the form form equal self test form then it sets up some clean data now when you
are doing an integration test that's already done somewhere else but when you we're just going to be calling clean so clean data doesn't exist so we need to make that that's a dependency so we need to set that up and we'll put some clean data in there and the first one there is no end date so that would throw an exception we're testing for only a start date
then it says result equals form clean 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 and 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 file 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 is not supposed to do that now if I if I wanted to pass it if I want to throw the exception and not capture it I could use an assertion called assert raises and put the exception in there and then the test will pass if an exception is thrown that's not captured so I can
whether I need to throw the exception or not I can test specifically for that and then the final thing is this last assertion assertion is is the result ABC we go back and have a look at the code it takes a result if 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 so 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 methods doing so anything that my methods doing I want to test that and that's what that final assertion the
certain result ABC needs to test if I change the return value back on the patch at the top line to be if then my assertion will file unless I test for DEF so that's the idea that's the concept of detailed unit testing so
going on it's just talking about this at a more conceptual level so methods do things they and we want to try and capture each thing that it does so some of it is direct in method actions such as assignments or doing some sort of mathematics or whatever and the other thing that they typically do
is call stuff and most often but not always that's a call to super but 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 feeding to this function if you say if you only sometimes call super based on certain inputs then you 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 it's 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 has this thing called call args and call args lists 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 put in star args but anything that's called as a positional argument the second index is anything that you call it's 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 class is actually not passed as the first positional key word so first positional
arg even though you actually physically write CLS or whatever to call it in the code and if it's a static method where you're expecting not nothing to be passed to that stage call args list is used when you're calling the function more than once it allows you to to make assertions on the parameters for
each call so next thing is mixins so we discussed with mixins a little bit before and so if you write a mixin and you you'll be wanting to test a method its method so in this case we've got a validation object which is designed it's a mixin so it's designed to work with other with other
stuff and it has an init method that does some stuff so to test that in our test server val mixin which is not hitting the database so we can use simple test case to make things a bit faster so we create a dummy field
that inherits from the test class that's the class that's under test plus char field that field doesn't need to do it that class doesn't need to do anything else so that's why we've got pass there and when we create this field we're going to be calling the init method but we don't want to call
the init method of char field because I don't care what goes on inside that I'm not testing Django I'm testing my code so I don't want to I just I need to patch that thing and so wherever the top the wherever serve our mixins define that's the thing I need put next to patch object module we're
imported to not where it's defined where it's imported to the init method auto spec equals true and it doesn't return anything so don't any return baby so then in the test I create the test field you could see I'm using keyword args the double star I'm passing in whatever doesn't matter what I'm
just passing in some dictionary but this init method is supposed to pass that on this test cases is testing where I provide no settings to the to the actual mixing but the mixing is used in the context of where it's mixed in with something else never use it alone and so one of the assertions which is the
bottom one test to see whether the init method of the of the base class was call with that 11 and 12 dictionary that's what that bottom line is doing so I'm actually seeing I'm testing the super call in the init method and if
it doesn't pass it on then my assertion will fail so just a few comments about mocks by default when you when you create a mock everything's defined so if you're testing that it's not there you need to delete it which is under so in this this test case here we see online three and four the Dell
mock one dot save and Dell to mock so this is testing something that is a mixing that could be used with a model form but it could be used in an ordinary form and model forms have got a save method and ordinary forms don't
well what happens if I want to test it with something that has it and something that doesn't and is it going to work or is it going to throw an exception and so this this test method tests for that it's setting up a view that's got some forms it creates a dictionary of forms form one
form two and it then passes that to the form valid method of the thing that's under test another note is up the top you see there's three patches it's patching the get success URL because that's what formed out if you in you call form valid call form valid calls the get
success URL so but I don't want it to do that I don't want it to go through the class base view hierarchy I just want it to to do what I need this thing to do I don't want it to redirect I don't it's calling the it's it's
saving several forms it's doing it in a transaction is that transaction actually invoked and the assertion that tests for that is the fourth up from the bottom one little quick tip when it comes to these patches when there's more than one of them the order unique it's like a mirror image the closest one in is the first the ta patch then the next one up is the
HR patch you need to do them in a mirror order not the top patch is the first positional argument after self you do them like in a in a mirror image is the best way I found to think about that so we're getting low on time so
just a quick a quick look at template unit testing so here's a dictionary object that but the base test just does what Django does but it also does indirect lookups so I test it to see whether it can do those things or not so all I need to do is to set up a template with a minimal string
that just load the test tags and then actually invokes the tag set up 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 alright so in summary a unit test is focused on a
specific method and that's it whatever that method is doing directly or what it's calling how when I say when it 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 you 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 these slides up they've got the Twitter handle there so I've got them hosted somewhere and so you're testing on arguments to test the cohesion and you use dummy classes subclasses to text to test mixing mocks
you can have they have attributes which we didn't get too much time to go into and you need to delete attributes and methods where you need to test for their absence so we've got any questions 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 and do you find that there's an additional maintenance burden by getting into that much detail that you're that you're doing I mean the tests look very nice but I worry about maintenance look it it does exist but it's minimal and 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 your 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 to Django to the other
the problems that can arise from that where you 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 your testing so the problems are really when
they do arise with your tested code not your testing code and you want your tests to fail in that situation so I have a question about how do you go about deciding if an integration test would be better for a particular view
rather than you know 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 from use case text so I want to see that those stories are captured in the
integration tests and then the unit tests focus on the detail of each function so I don't do a trade-off there I actually do both and 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 so in the end
it ends up working quite quickly thank you again Wayne