{"title":"Writing your first Django app, part 5","version":"2.0","locale":"en","docname":"intro/tutorial05","url":"/en/2.0/intro/tutorial05/","canonical":"https://djangodocs.dev/en/2.0/intro/tutorial05/","summary":"This tutorial begins where Tutorial 4 left off. We’ve built a Web-poll application, and we’ll now create some automated tests for it. Introducing automated testing…","html":"<h1>Writing your first Django app, part 5<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-5\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>This tutorial begins where <a class=\"reference internal\" href=\"/en/2.0/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a> left off.\nWe’ve built a Web-poll application, and we’ll now create some automated tests\nfor it.</p>\n<section id=\"introducing-automated-testing\">\n<h2>Introducing automated testing<a class=\"heading-anchor\" href=\"#introducing-automated-testing\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"what-are-automated-tests\">\n<h3>What are automated tests?<a class=\"heading-anchor\" href=\"#what-are-automated-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Tests are simple routines that check the operation of your code.</p>\n<p>Testing operates at different levels. Some tests might apply to a tiny detail\n(<em>does a particular model method return values as expected?</em>) while others\nexamine the overall operation of the software (<em>does a sequence of user inputs\non the site produce the desired result?</em>). That’s no different from the kind of\ntesting you did earlier in <a class=\"reference internal\" href=\"/en/2.0/intro/tutorial02/\"><span class=\"doc\">Tutorial 2</span></a>, using the\n<a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> to examine the behavior of a method, or running the\napplication and entering data to check how it behaves.</p>\n<p>What’s different in <em>automated</em> tests is that the testing work is done for\nyou by the system. You create a set of tests once, and then as you make changes\nto your app, you can check that your code still works as you originally\nintended, without having to perform time consuming manual testing.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>Why you need to create tests<a class=\"heading-anchor\" href=\"#why-you-need-to-create-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>So why create tests, and why now?</p>\n<p>You may feel that you have quite enough on your plate just learning\nPython/Django, and having yet another thing to learn and do may seem\noverwhelming and perhaps unnecessary. After all, our polls application is\nworking quite happily now; going through the trouble of creating automated\ntests is not going to make it work any better. If creating the polls\napplication is the last bit of Django programming you will ever do, then true,\nyou don’t need to know how to create automated tests. But, if that’s not the\ncase, now is an excellent time to learn.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>Tests will save you time<a class=\"heading-anchor\" href=\"#tests-will-save-you-time\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Up to a certain point, ‘checking that it seems to work’ will be a satisfactory\ntest. In a more sophisticated application, you might have dozens of complex\ninteractions between components.</p>\n<p>A change in any of those components could have unexpected consequences on the\napplication’s behavior. Checking that it still ‘seems to work’ could mean\nrunning through your code’s functionality with twenty different variations of\nyour test data just to make sure you haven’t broken something - not a good use\nof your time.</p>\n<p>That’s especially true when automated tests could do this for you in seconds.\nIf something’s gone wrong, tests will also assist in identifying the code\nthat’s causing the unexpected behavior.</p>\n<p>Sometimes it may seem a chore to tear yourself away from your productive,\ncreative programming work to face the unglamorous and unexciting business\nof writing tests, particularly when you know your code is working properly.</p>\n<p>However, the task of writing tests is a lot more fulfilling than spending hours\ntesting your application manually or trying to identify the cause of a\nnewly-introduced problem.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>Tests don’t just identify problems, they prevent them<a class=\"heading-anchor\" href=\"#tests-don-t-just-identify-problems-they-prevent-them\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>It’s a mistake to think of tests merely as a negative aspect of development.</p>\n<p>Without tests, the purpose or intended behavior of an application might be\nrather opaque. Even when it’s your own code, you will sometimes find yourself\npoking around in it trying to find out what exactly it’s doing.</p>\n<p>Tests change that; they light up your code from the inside, and when something\ngoes wrong, they focus light on the part that has gone wrong - <em>even if you\nhadn’t even realized it had gone wrong</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>Tests make your code more attractive<a class=\"heading-anchor\" href=\"#tests-make-your-code-more-attractive\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>You might have created a brilliant piece of software, but you will find that\nmany other developers will simply refuse to look at it because it lacks tests;\nwithout tests, they won’t trust it. Jacob Kaplan-Moss, one of Django’s\noriginal developers, says “Code without tests is broken by design.”</p>\n<p>That other developers want to see tests in your software before they take it\nseriously is yet another reason for you to start writing tests.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>Tests help teams work together<a class=\"heading-anchor\" href=\"#tests-help-teams-work-together\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>The previous points are written from the point of view of a single developer\nmaintaining an application. Complex applications will be maintained by teams.\nTests guarantee that colleagues don’t inadvertently break your code (and that\nyou don’t break theirs without knowing). If you want to make a living as a\nDjango programmer, you must be good at writing tests!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>Basic testing strategies<a class=\"heading-anchor\" href=\"#basic-testing-strategies\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>There are many ways to approach writing tests.</p>\n<p>Some programmers follow a discipline called “<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Test-driven_development\">test-driven development</a>”; they\nactually write their tests before they write their code. This might seem\ncounter-intuitive, but in fact it’s similar to what most people will often do\nanyway: they describe a problem, then create some code to solve it. Test-driven\ndevelopment simply formalizes the problem in a Python test case.</p>\n<p>More often, a newcomer to testing will create some code and later decide that\nit should have some tests. Perhaps it would have been better to write some\ntests earlier, but it’s never too late to get started.</p>\n<p>Sometimes it’s difficult to figure out where to get started with writing tests.\nIf you have written several thousand lines of Python, choosing something to\ntest might not be easy. In such a case, it’s fruitful to write your first test\nthe next time you make a change, either when you add a new feature or fix a bug.</p>\n<p>So let’s do that right away.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>Writing our first test<a class=\"heading-anchor\" href=\"#writing-our-first-test\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"we-identify-a-bug\">\n<h3>We identify a bug<a class=\"heading-anchor\" href=\"#we-identify-a-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Fortunately, there’s a little bug in the <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> application for us to fix\nright away: the <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> method returns <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> if\nthe <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> was published within the last day (which is correct) but also if\nthe <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>’s <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> field is in the future (which certainly isn’t).</p>\n<p>To check if the bug really exists, using the Admin create a question whose date\nlies in the future and check the method using the <a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">datetime</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">polls.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create a Question instance with pub_date 30 days in the future</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># was it published recently?</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">()</span>\n<span class=\"go\">True</span>\n</code></pre></div>\n<p>Since things in the future are not ‘recent’, this is clearly wrong.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>Create a test to expose the bug<a class=\"heading-anchor\" href=\"#create-a-test-to-expose-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>What we’ve just done in the <a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> to test for the problem is exactly\nwhat we can do in an automated test, so let’s turn that into an automated test.</p>\n<p>A conventional place for an application’s tests is in the application’s\n<code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> file; the testing system will automatically find tests in any file\nwhose name begins with <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>.</p>\n<p>Put the following in the <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> file in the <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> application:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/tests.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/tests.py\"><code><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">datetime</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">TestCase</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionModelTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        was_published_recently() returns False for questions whose pub_date</span>\n<span class=\"sd\">        is in the future.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>What we have done here is created a <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> subclass\nwith a method that creates a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> instance with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the\nfuture. We then check the output of <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> - which\n<em>ought</em> to be False.</p>\n</section>\n<section id=\"running-tests\">\n<h3>Running tests<a class=\"heading-anchor\" href=\"#running-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>In the terminal, we can run our test:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>$ python manage.py test polls\n</code></pre></div>\n<p>and you’ll see something like:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"n\">F</span>\n<span class=\"o\">======================================================================</span>\n<span class=\"n\">FAIL</span><span class=\"p\">:</span> <span class=\"n\">test_was_published_recently_with_future_question</span> <span class=\"p\">(</span><span class=\"n\">polls</span><span class=\"o\">.</span><span class=\"n\">tests</span><span class=\"o\">.</span><span class=\"n\">QuestionModelTests</span><span class=\"p\">)</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Traceback</span> <span class=\"p\">(</span><span class=\"n\">most</span> <span class=\"n\">recent</span> <span class=\"n\">call</span> <span class=\"n\">last</span><span class=\"p\">):</span>\n  <span class=\"n\">File</span> <span class=\"s2\">&quot;/path/to/mysite/polls/tests.py&quot;</span><span class=\"p\">,</span> <span class=\"n\">line</span> <span class=\"mi\">16</span><span class=\"p\">,</span> <span class=\"ow\">in</span> <span class=\"n\">test_was_published_recently_with_future_question</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n<span class=\"ne\">AssertionError</span><span class=\"p\">:</span> <span class=\"kc\">True</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"kc\">False</span>\n\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">FAILED</span> <span class=\"p\">(</span><span class=\"n\">failures</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>What happened is this:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">python</span> <span class=\"pre\">manage.py</span> <span class=\"pre\">test</span> <span class=\"pre\">polls</span></code> looked for tests in the <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> application</p></li>\n<li><p>it found a subclass of the <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> class</p></li>\n<li><p>it created a special database for the purpose of testing</p></li>\n<li><p>it looked for test methods - ones whose names begin with <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code></p></li>\n<li><p>in <code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code> it created a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>\ninstance whose <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> field is 30 days in the future</p></li>\n<li><p>… and using the <code class=\"docutils literal notranslate\"><span class=\"pre\">assertIs()</span></code> method, it discovered that its\n<code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> returns <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>, though we wanted it to return\n<code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code></p></li>\n</ul>\n<p>The test informs us which test failed and even the line on which the failure\noccurred.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>Fixing the bug<a class=\"heading-anchor\" href=\"#fixing-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>We already know what the problem is: <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> should\nreturn <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> if its <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> is in the future. Amend the method in\n<code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>, so that it will only return <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> if the date is also in the\npast:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/models.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/models.py\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">was_published_recently</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n    <span class=\"n\">now</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span>\n    <span class=\"k\">return</span> <span class=\"n\">now</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">&lt;=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">pub_date</span> <span class=\"o\">&lt;=</span> <span class=\"n\">now</span>\n</code></pre></figure>\n<p>and run the test again:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"o\">.</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">OK</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>After identifying a bug, we wrote a test that exposes it and corrected the bug\nin the code so our test passes.</p>\n<p>Many other things might go wrong with our application in the future, but we can\nbe sure that we won’t inadvertently reintroduce this bug, because simply\nrunning the test will warn us immediately. We can consider this little portion\nof the application pinned down safely forever.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>More comprehensive tests<a class=\"heading-anchor\" href=\"#more-comprehensive-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>While we’re here, we can further pin down the <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>\nmethod; in fact, it would be positively embarrassing if in fixing one bug we had\nintroduced another.</p>\n<p>Add two more test methods to the same class, to test the behavior of the method\nmore comprehensively:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/tests.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/tests.py\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_old_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    was_published_recently() returns False for questions whose pub_date</span>\n<span class=\"sd\">    is older than 1 day.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"n\">seconds</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n    <span class=\"n\">old_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">old_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_recent_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    was_published_recently() returns True for questions whose pub_date</span>\n<span class=\"sd\">    is within the last day.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">hours</span><span class=\"o\">=</span><span class=\"mi\">23</span><span class=\"p\">,</span> <span class=\"n\">minutes</span><span class=\"o\">=</span><span class=\"mi\">59</span><span class=\"p\">,</span> <span class=\"n\">seconds</span><span class=\"o\">=</span><span class=\"mi\">59</span><span class=\"p\">)</span>\n    <span class=\"n\">recent_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">recent_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">True</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>And now we have three tests that confirm that <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code>\nreturns sensible values for past, recent, and future questions.</p>\n<p>Again, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> is a simple application, but however complex it grows in the\nfuture and whatever other code it interacts with, we now have some guarantee\nthat the method we have written tests for will behave in expected ways.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>Test a view<a class=\"heading-anchor\" href=\"#test-a-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The polls application is fairly undiscriminating: it will publish any question,\nincluding ones whose <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> field lies in the future. We should improve\nthis. Setting a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the future should mean that the Question is\npublished at that moment, but invisible until then.</p>\n<section id=\"a-test-for-a-view\">\n<h3>A test for a view<a class=\"heading-anchor\" href=\"#a-test-for-a-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>When we fixed the bug above, we wrote the test first and then the code to fix\nit. In fact that was a simple example of test-driven development, but it\ndoesn’t really matter in which order we do the work.</p>\n<p>In our first test, we focused closely on the internal behavior of the code. For\nthis test, we want to check its behavior as it would be experienced by a user\nthrough a web browser.</p>\n<p>Before we try to fix anything, let’s have a look at the tools at our disposal.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>The Django test client<a class=\"heading-anchor\" href=\"#the-django-test-client\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Django provides a test <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.Client\" title=\"django.test.Client\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Client</span></code></a> to simulate a user\ninteracting with the code at the view level.  We can use it in <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>\nor even in the <a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>.</p>\n<p>We will start again with the <a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>, where we need to do a couple of\nthings that won’t be necessary in <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. The first is to set up the test\nenvironment in the <a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">setup_test_environment</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">setup_test_environment</span><span class=\"p\">()</span>\n</code></pre></div>\n<p><a class=\"reference internal\" href=\"/en/2.0/topics/testing/advanced/#django.test.utils.setup_test_environment\" title=\"django.test.utils.setup_test_environment\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">setup_test_environment()</span></code></a> installs a template renderer\nwhich will allow us to examine some additional attributes on responses such as\n<code class=\"docutils literal notranslate\"><span class=\"pre\">response.context</span></code> that otherwise wouldn’t be available. Note that this\nmethod <em>does not</em> setup a test database, so the following will be run against\nthe existing database and the output may differ slightly depending on what\nquestions you already created. You might get unexpected results if your\n<code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code> in <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code> isn’t correct. If you don’t remember setting\nit earlier, check it before continuing.</p>\n<p>Next we need to import the test client class (later in <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> we will use\nthe <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> class, which comes with its own client, so\nthis won’t be required):</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Client</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create an instance of the client for our use</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">client</span> <span class=\"o\">=</span> <span class=\"n\">Client</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>With that ready, we can ask the client to do some work for us:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># get a response from &#39;/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&#39;/&#39;</span><span class=\"p\">)</span>\n<span class=\"go\">Not Found: /</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we should expect a 404 from that address; if you instead see an</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># &quot;Invalid HTTP_HOST header&quot; error and a 400 response, you probably</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># omitted the setup_test_environment() call described earlier.</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">404</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># on the other hand we should expect to find something at &#39;/polls/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we&#39;ll use &#39;reverse()&#39; rather than a hardcoded URL</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">200</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">content</span>\n<span class=\"go\">b&#39;\\n    &lt;ul&gt;\\n    \\n        &lt;li&gt;&lt;a href=&quot;/polls/1/&quot;&gt;What&amp;#39;s up?&lt;/a&gt;&lt;/li&gt;\\n    \\n    &lt;/ul&gt;\\n\\n&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">]</span>\n<span class=\"go\">&lt;QuerySet [&lt;Question: What&#39;s up?&gt;]&gt;</span>\n</code></pre></div>\n</section>\n<section id=\"improving-our-view\">\n<h3>Improving our view<a class=\"heading-anchor\" href=\"#improving-our-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The list of polls shows polls that aren’t published yet (i.e. those that have a\n<code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the future). Let’s fix that.</p>\n<p>In <a class=\"reference internal\" href=\"/en/2.0/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a> we introduced a class-based view,\nbased on <a class=\"reference internal\" href=\"/en/2.0/ref/class-based-views/generic-display/#django.views.generic.list.ListView\" title=\"django.views.generic.list.ListView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ListView</span></code></a>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/views.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/views.py\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">IndexView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">ListView</span><span class=\"p\">):</span>\n    <span class=\"n\">template_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls/index.html&#39;</span>\n    <span class=\"n\">context_object_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;latest_question_list&#39;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;Return the last five published questions.&quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p>We need to amend the <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> method and change it so that it also\nchecks the date by comparing it with <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>. First we need to add\nan import:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/views.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/views.py\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n</code></pre></figure>\n<p>and then we must amend the <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> method like so:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/views.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/views.py\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    Return the last five published questions (not including those set to be</span>\n<span class=\"sd\">    published in the future).</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span>\n        <span class=\"n\">pub_date__lte</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span>\n    <span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">Question.objects.filter(pub_date__lte=timezone.now())</span></code> returns a queryset\ncontaining <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>s whose <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> is less than or equal to - that\nis, earlier than or equal to - <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now</span></code>.</p>\n</section>\n<section id=\"testing-our-new-view\">\n<h3>Testing our new view<a class=\"heading-anchor\" href=\"#testing-our-new-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Now you can satisfy yourself that this behaves as expected by firing up the\nrunserver, loading the site in your browser, creating <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> with dates\nin the past and future, and checking that only those that have been published\nare listed.  You don’t want to have to do that <em>every single time you make any\nchange that might affect this</em> - so let’s also create a test, based on our\n<a class=\"reference internal\" href=\"/en/2.0/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> session above.</p>\n<p>Add the following to <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/tests.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/tests.py\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n</code></pre></figure>\n<p>and we’ll create a shortcut function to create questions as well as a new test\nclass:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/tests.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/tests.py\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    Create a question with the given `question_text` and published the</span>\n<span class=\"sd\">    given number of `days` offset to now (negative for questions published</span>\n<span class=\"sd\">    in the past, positive for questions that have yet to be published).</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"n\">days</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"n\">question_text</span><span class=\"p\">,</span> <span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionIndexViewTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_no_questions</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        If no questions exist, an appropriate message is displayed.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">200</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the past are displayed on the</span>\n<span class=\"sd\">        index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the future aren&#39;t displayed on</span>\n<span class=\"sd\">        the index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question_and_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Even if both past and future questions exist, only past questions</span>\n<span class=\"sd\">        are displayed.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_two_past_questions</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The questions index page may display multiple questions.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 1.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 2.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question 2.&gt;&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;&lt;Question: Past question 1.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n</code></pre></figure>\n<p>Let’s look at some of these more closely.</p>\n<p>First is a question shortcut function, <code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>, to take some\nrepetition out of the process of creating questions.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code> doesn’t create any questions, but checks the message:\n“No polls are available.” and verifies the <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> is empty.\nNote that the <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> class provides some additional\nassertion methods. In these examples, we use\n<a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.SimpleTestCase.assertContains\" title=\"django.test.SimpleTestCase.assertContains\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">assertContains()</span></code></a> and\n<a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual\" title=\"django.test.TransactionTestCase.assertQuerysetEqual\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">assertQuerysetEqual()</span></code></a>.</p>\n<p>In <code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code>, we create a question and verify that it appears in\nthe list.</p>\n<p>In <code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code>, we create a question with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the\nfuture. The database is reset for each test method, so the first question is no\nlonger there, and so again the index shouldn’t have any questions in it.</p>\n<p>And so on. In effect, we are using the tests to tell a story of admin input\nand user experience on the site, and checking that at every state and for every\nnew change in the state of the system, the expected results are published.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3>Testing the <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code><a class=\"heading-anchor\" href=\"#testing-the-detailview\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>What we have works well; however, even though future questions don’t appear in\nthe <em>index</em>, users can still reach them if they know or guess the right URL. So\nwe need to add a similar  constraint to <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/views.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/views.py\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">DetailView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">DetailView</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Excludes any questions that aren&#39;t published yet.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span><span class=\"n\">pub_date__lte</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">())</span>\n</code></pre></figure>\n<p>And of course, we will add some tests, to check that a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> whose\n<code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> is in the past can be displayed, and that one with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>\nin the future is not:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>polls/tests.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"polls/tests.py\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionDetailViewTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The detail view of a question with a pub_date in the future</span>\n<span class=\"sd\">        returns a 404 not found.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s1\">&#39;Future question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">404</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The detail view of a question with a pub_date in the past</span>\n<span class=\"sd\">        displays the question&#39;s text.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">past_question</span> <span class=\"o\">=</span> <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s1\">&#39;Past Question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">question_text</span><span class=\"p\">)</span>\n</code></pre></figure>\n</section>\n<section id=\"ideas-for-more-tests\">\n<h3>Ideas for more tests<a class=\"heading-anchor\" href=\"#ideas-for-more-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>We ought to add a similar <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> method to <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code> and\ncreate a new test class for that view. It’ll be very similar to what we have\njust created; in fact there will be a lot of repetition.</p>\n<p>We could also improve our application in other ways, adding tests along the\nway. For example, it’s silly that <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> can be published on the site\nthat have no <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code>. So, our views could check for this, and exclude such\n<code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>. Our tests would create a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> without <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> and\nthen test that it’s not published, as well as create a similar <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>\n<em>with</em> <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code>, and test that it <em>is</em> published.</p>\n<p>Perhaps logged-in admin users should be allowed to see unpublished\n<code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>, but not ordinary visitors. Again: whatever needs to be added to\nthe software to accomplish this should be accompanied by a test, whether you\nwrite the test first and then make the code pass the test, or work out the\nlogic in your code first and then write a test to prove it.</p>\n<p>At a certain point you are bound to look at your tests and wonder whether your\ncode is suffering from test bloat, which brings us to:</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>When testing, more is better<a class=\"heading-anchor\" href=\"#when-testing-more-is-better\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>It might seem that our tests are growing out of control. At this rate there will\nsoon be more code in our tests than in our application, and the repetition\nis unaesthetic, compared to the elegant conciseness of the rest of our code.</p>\n<p><strong>It doesn’t matter</strong>. Let them grow. For the most part, you can write a test\nonce and then forget about it. It will continue performing its useful function\nas you continue to develop your program.</p>\n<p>Sometimes tests will need to be updated. Suppose that we amend our views so that\nonly <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> with <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> are published. In that case, many of our\nexisting tests will fail - <em>telling us exactly which tests need to be amended to\nbring them up to date</em>, so to that extent tests help look after themselves.</p>\n<p>At worst, as you continue developing, you might find that you have some tests\nthat are now redundant. Even that’s not a problem; in testing redundancy is\na <em>good</em> thing.</p>\n<p>As long as your tests are sensibly arranged, they won’t become unmanageable.\nGood rules-of-thumb include having:</p>\n<ul class=\"simple\">\n<li><p>a separate <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code> for each model or view</p></li>\n<li><p>a separate test method for each set of conditions you want to test</p></li>\n<li><p>test method names that describe their function</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>Further testing<a class=\"heading-anchor\" href=\"#further-testing\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>This tutorial only introduces some of the basics of testing. There’s a great\ndeal more you can do, and a number of very useful tools at your disposal to\nachieve some very clever things.</p>\n<p>For example, while our tests here have covered some of the internal logic of a\nmodel and the way our views publish information, you can use an “in-browser”\nframework such as <a class=\"reference external\" href=\"http://seleniumhq.org/\">Selenium</a> to test the way your HTML actually renders in a\nbrowser. These tools allow you to check not just the behavior of your Django\ncode, but also, for example, of your JavaScript. It’s quite something to see\nthe tests launch a browser, and start interacting with your site, as if a human\nbeing were driving it! Django includes <a class=\"reference internal\" href=\"/en/2.0/topics/testing/tools/#django.test.LiveServerTestCase\" title=\"django.test.LiveServerTestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">LiveServerTestCase</span></code></a>\nto facilitate integration with tools like Selenium.</p>\n<p>If you have a complex application, you may want to run tests automatically\nwith every commit for the purposes of <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Continuous_integration\">continuous integration</a>, so that\nquality control is itself - at least partially - automated.</p>\n<p>A good way to spot untested parts of your application is to check code\ncoverage. This also helps identify fragile or even dead code. If you can’t test\na piece of code, it usually means that code should be refactored or removed.\nCoverage will help to identify dead code. See\n<a class=\"reference internal\" href=\"/en/2.0/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Integration with coverage.py</span></a> for details.</p>\n<p><a class=\"reference internal\" href=\"/en/2.0/topics/testing/\"><span class=\"doc\">Testing in Django</span></a> has comprehensive\ninformation about testing.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>What’s next?<a class=\"heading-anchor\" href=\"#what-s-next\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>For full details on testing, see <a class=\"reference internal\" href=\"/en/2.0/topics/testing/\"><span class=\"doc\">Testing in Django</span></a>.</p>\n<p>When you’re comfortable with testing Django views, read\n<a class=\"reference internal\" href=\"/en/2.0/intro/tutorial06/\"><span class=\"doc\">part 6 of this tutorial</span></a> to learn about\nstatic files management.</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"Introducing automated testing","anchor":"introducing-automated-testing","children":[{"title":"What are automated tests?","anchor":"what-are-automated-tests","children":[]},{"title":"Why you need to create tests","anchor":"why-you-need-to-create-tests","children":[{"title":"Tests will save you time","anchor":"tests-will-save-you-time","children":[]},{"title":"Tests don’t just identify problems, they prevent them","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"Tests make your code more attractive","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"Tests help teams work together","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"Basic testing strategies","anchor":"basic-testing-strategies","children":[]},{"title":"Writing our first test","anchor":"writing-our-first-test","children":[{"title":"We identify a bug","anchor":"we-identify-a-bug","children":[]},{"title":"Create a test to expose the bug","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"Running tests","anchor":"running-tests","children":[]},{"title":"Fixing the bug","anchor":"fixing-the-bug","children":[]},{"title":"More comprehensive tests","anchor":"more-comprehensive-tests","children":[]}]},{"title":"Test a view","anchor":"test-a-view","children":[{"title":"A test for a view","anchor":"a-test-for-a-view","children":[]},{"title":"The Django test client","anchor":"the-django-test-client","children":[]},{"title":"Improving our view","anchor":"improving-our-view","children":[]},{"title":"Testing our new view","anchor":"testing-our-new-view","children":[]},{"title":"Testing the DetailView","anchor":"testing-the-detailview","children":[]},{"title":"Ideas for more tests","anchor":"ideas-for-more-tests","children":[]}]},{"title":"When testing, more is better","anchor":"when-testing-more-is-better","children":[]},{"title":"Further testing","anchor":"further-testing","children":[]},{"title":"What’s next?","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"Getting started","url":"/en/2.0/intro/"}],"prev":{"docname":"intro/tutorial04","title":"Writing your first Django app, part 4","url":"/en/2.0/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"Writing your first Django app, part 6","url":"/en/2.0/intro/tutorial06/"},"formats":{"html":"/en/2.0/intro/tutorial05/","markdown":"/en/2.0/intro/tutorial05.md","json":"/en/2.0/intro/tutorial05.json"},"source":"https://github.com/django/django/blob/stable/2.0.x/docs/intro/tutorial05.txt","official":"https://docs.djangoproject.com/en/2.0/intro/tutorial05/","inVersions":["dev","6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10","1.9","1.8"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}