{"title":"Skriva din första Django-app, del 5","version":"5.2","locale":"sv","docname":"intro/tutorial05","url":"/sv/5.2/intro/tutorial05/","canonical":"https://djangodocs.dev/sv/5.2/intro/tutorial05/","summary":"Denna handledning börjar där Tutorial 4 slutade. Vi har byggt en applikation för webbenkäter och nu ska vi skapa några automatiserade tester för den. Var du kan få…","html":"<h1>Skriva din första Django-app, del 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>Denna handledning börjar där <a class=\"reference internal\" href=\"/sv/5.2/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a> slutade. Vi har byggt en applikation för webbenkäter och nu ska vi skapa några automatiserade tester för den.</p>\n<aside class=\"admonition-where-to-get-help admonition\">\n<p class=\"admonition-title\">Var du kan få hjälp:</p>\n<p>Om du har problem med att gå igenom den här handledningen kan du gå till avsnittet <a class=\"reference internal\" href=\"/sv/5.2/faq/help/\"><span class=\"doc\">Att få hjälp</span></a> i FAQ.</p>\n</aside>\n<section id=\"introducing-automated-testing\">\n<h2>Introduktion av automatiserad testning<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>Vad är automatiserade tester?<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>Tester är rutiner som kontrollerar att din kod fungerar som den ska.</p>\n<p>Testning sker på olika nivåer. Vissa tester kan gälla en liten detalj (<em>återför en viss modellmetod värden som förväntat?</em>) medan andra undersöker programvarans övergripande funktion (<em>ger en sekvens av användarinmatningar på webbplatsen önskat resultat?</em>). Det skiljer sig inte från den typ av testning du gjorde tidigare i <a class=\"reference internal\" href=\"/sv/5.2/intro/tutorial02/\"><span class=\"doc\">Tutorial 2</span></a>, där du använde <a class=\"reference internal\" href=\"/sv/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> för att undersöka hur en metod fungerar, eller körde programmet och matade in data för att kontrollera hur det fungerar.</p>\n<p>Det som är annorlunda med <em>automatiserade</em> tester är att testarbetet görs åt dig av systemet. Du skapar en uppsättning tester en gång, och när du sedan gör ändringar i din app kan du kontrollera att din kod fortfarande fungerar som du ursprungligen avsåg, utan att behöva utföra tidskrävande manuell testning.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>Varför du behöver skapa tester<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>Så varför skapa tester, och varför just nu?</p>\n<p>Du kanske tycker att du har tillräckligt att göra bara med att lära dig Python/Django, och att ha ännu en sak att lära sig och göra kan verka överväldigande och kanske onödigt. När allt kommer omkring fungerar vår pollsapplikation ganska lyckligt nu; att gå igenom besväret med att skapa automatiserade tester kommer inte att få det att fungera bättre. Om skapandet av omröstningsapplikationen är den sista delen av Django-programmering som du någonsin kommer att göra, då är det sant att du inte behöver veta hur man skapar automatiserade tester. Men om så inte är fallet är det nu en utmärkt tid att lära sig.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>Tester sparar tid<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>Upp till en viss punkt är det tillräckligt att ”kontrollera att det verkar fungera”. I en mer sofistikerad applikation kan du ha dussintals komplexa interaktioner mellan komponenter.</p>\n<p>En förändring i någon av dessa komponenter kan få oväntade konsekvenser för applikationens beteende. Att kontrollera att det fortfarande ”verkar fungera” kan innebära att du kör igenom din kods funktionalitet med tjugo olika variationer av dina testdata för att se till att du inte har brutit något - inte en bra användning av din tid.</p>\n<p>Det är särskilt sant när automatiserade tester kan göra detta åt dig på några sekunder. Om något har gått fel hjälper testerna också till att identifiera koden som orsakar det oväntade beteendet.</p>\n<p>Ibland kan det kännas jobbigt att slita sig från det produktiva och kreativa programmeringsarbetet för att ägna sig åt det oglamorösa och ospännande arbetet med att skriva tester, särskilt när man vet att koden fungerar som den ska.</p>\n<p>Att skriva tester är dock mycket mer tillfredsställande än att spendera timmar på att testa din applikation manuellt eller att försöka identifiera orsaken till ett nytillkommet problem.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>Tester identifierar inte bara problem, de förebygger dem<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>Det är ett misstag att bara se tester som en negativ aspekt av utveckling.</p>\n<p>Utan tester kan syftet med eller det avsedda beteendet hos en applikation vara ganska oklart. Även om det är din egen kod kommer du ibland att behöva rota runt i den för att försöka ta reda på exakt vad den gör.</p>\n<p>Tester ändrar på det; de lyser upp din kod från insidan, och när något går fel fokuserar de ljuset på den del som har gått fel - <em>även om du inte ens hade insett att det hade gått fel</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>Tester gör din kod mer attraktiv<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>Du kanske har skapat en briljant programvara, men du kommer att upptäcka att många andra utvecklare vägrar att titta på den eftersom den saknar tester; utan tester litar de inte på den. Jacob Kaplan-Moss, en av Djangos ursprungliga utvecklare, säger ”Code without tests is broken by design”</p>\n<p>Att andra utvecklare vill se tester i din programvara innan de tar den på allvar är ytterligare ett skäl för dig att börja skriva tester.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>Tester hjälper team att arbeta tillsammans<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>De föregående punkterna är skrivna ur en enskild utvecklares synvinkel som underhåller en applikation. Komplexa applikationer kommer att underhållas av team. Tester garanterar att kollegor inte oavsiktligt bryter din kod (och att du inte bryter deras utan att veta om det). Om du vill kunna försörja dig som Django-programmerare måste du vara bra på att skriva tester!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>Grundläggande teststrategier<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>Det finns många olika sätt att skriva tester på.</p>\n<p>Vissa programmerare följer en disciplin som kallas ”testdriven utveckling”; de skriver faktiskt sina tester innan de skriver sin kod. Detta kan verka kontraintuitivt, men i själva verket liknar det vad de flesta människor ofta kommer att göra ändå: de beskriver ett problem och skapar sedan lite kod för att lösa det. Testdriven utveckling formaliserar problemet i ett Python-testfall.</p>\n<p>Oftare skapar en nybörjare lite kod och bestämmer sig senare för att den borde ha några tester. Kanske hade det varit bättre att skriva några tester tidigare, men det är aldrig för sent att komma igång.</p>\n<p>Ibland är det svårt att komma på var man ska börja skriva tester. Om du har skrivit flera tusen rader Python är det kanske inte så lätt att välja något att testa. I ett sådant fall är det fruktbart att skriva ditt första test nästa gång du gör en förändring, antingen när du lägger till en ny funktion eller fixar en bugg.</p>\n<p>Så låt oss göra det på en gång.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>Skriva vårt första 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>Vi identifierar ett fel<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>Lyckligtvis finns det en liten bugg i applikationen <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> som vi kan åtgärda direkt: metoden <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> returnerar <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> om <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> publicerades under den senaste dagen (vilket är korrekt) men också om <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> fältet <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> ligger i framtiden (vilket det verkligen inte gör).</p>\n<p>Bekräfta felet genom att använda <a class=\"reference internal\" href=\"/sv/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> för att kontrollera metoden på en fråga vars datum ligger i framtiden:</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-0-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</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=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span>shell\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-0-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</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=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py shell\n</code></pre></div></div></div>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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=\"Python console 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=\"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>Eftersom saker i framtiden inte är ”nyligen inträffade” är detta helt klart fel.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>Skapa ett test för att avslöja felet<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>Det vi just har gjort i <a class=\"reference internal\" href=\"/sv/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> för att testa problemet är exakt vad vi kan göra i ett automatiserat test, så låt oss göra det till ett automatiserat test.</p>\n<p>En konventionell plats för en applikations tester är i applikationens fil <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>; testsystemet hittar automatiskt tester i alla filer vars namn börjar med <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>.</p>\n<p>Skriv följande i filen <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> i applikationen <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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    <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>Här har vi skapat en <a class=\"reference internal\" href=\"/sv/5.2/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>-underklass med en metod som skapar en <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>-instans med ett <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> i framtiden. Vi kontrollerar sedan resultatet av <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> - som <em>bör</em> vara False.</p>\n</section>\n<section id=\"running-tests\">\n<h3>Genomföra tester<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>I terminalen kan vi köra vårt test:</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-1-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</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=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span><span class=\"nb\">test</span><span class=\"w\"> </span>polls\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-1-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</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=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py test polls\n</code></pre></div></div></div>\n<p>och du kommer att se något liknande:</p>\n<div class=\"code-block\" data-language=\"shell\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</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=\"Shell code\"><code>Creating<span class=\"w\"> </span><span class=\"nb\">test</span><span class=\"w\"> </span>database<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"w\"> </span><span class=\"nb\">alias</span><span class=\"w\"> </span><span class=\"s1\">&#39;default&#39;</span>...\nSystem<span class=\"w\"> </span>check<span class=\"w\"> </span>identified<span class=\"w\"> </span>no<span class=\"w\"> </span>issues<span class=\"w\"> </span><span class=\"o\">(</span><span class=\"m\">0</span><span class=\"w\"> </span>silenced<span class=\"o\">)</span>.\n<span class=\"nv\">F</span>\n<span class=\"o\">======================================================================</span>\nFAIL:<span class=\"w\"> </span>test_was_published_recently_with_future_question<span class=\"w\"> </span><span class=\"o\">(</span>polls.tests.QuestionModelTests<span class=\"o\">)</span>\n----------------------------------------------------------------------\nTraceback<span class=\"w\"> </span><span class=\"o\">(</span>most<span class=\"w\"> </span>recent<span class=\"w\"> </span>call<span class=\"w\"> </span>last<span class=\"o\">)</span>:\n<span class=\"w\">  </span>File<span class=\"w\"> </span><span class=\"s2\">&quot;/path/to/djangotutorial/polls/tests.py&quot;</span>,<span class=\"w\"> </span>line<span class=\"w\"> </span><span class=\"m\">16</span>,<span class=\"w\"> </span><span class=\"k\">in</span><span class=\"w\"> </span>test_was_published_recently_with_future_question\n<span class=\"w\">    </span>self.assertIs<span class=\"o\">(</span>future_question.was_published_recently<span class=\"o\">()</span>,<span class=\"w\"> </span>False<span class=\"o\">)</span>\nAssertionError:<span class=\"w\"> </span>True<span class=\"w\"> </span>is<span class=\"w\"> </span>not<span class=\"w\"> </span>False\n\n----------------------------------------------------------------------\nRan<span class=\"w\"> </span><span class=\"m\">1</span><span class=\"w\"> </span><span class=\"nb\">test</span><span class=\"w\"> </span><span class=\"k\">in</span><span class=\"w\"> </span><span class=\"m\">0</span>.001s\n\nFAILED<span class=\"w\"> </span><span class=\"o\">(</span><span class=\"nv\">failures</span><span class=\"o\">=</span><span class=\"m\">1</span><span class=\"o\">)</span>\nDestroying<span class=\"w\"> </span><span class=\"nb\">test</span><span class=\"w\"> </span>database<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"w\"> </span><span class=\"nb\">alias</span><span class=\"w\"> </span><span class=\"s1\">&#39;default&#39;</span>...\n</code></pre></div>\n<aside class=\"admonition-different-error admonition\">\n<p class=\"admonition-title\">Annat fel?</p>\n<p>Om du istället får ett <code class=\"docutils literal notranslate\"><span class=\"pre\">NameError</span></code> här, kan du ha missat ett steg i <a class=\"reference internal\" href=\"/sv/5.2/intro/tutorial02/#tutorial02-import-timezone\"><span class=\"std std-ref\">Part 2</span></a> där vi lade till import av <code class=\"docutils literal notranslate\"><span class=\"pre\">datetime</span></code> och <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone</span></code> till <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/models.py</span></code>. Kopiera importerna från det avsnittet och försök köra dina tester igen.</p>\n</aside>\n<p>Det som hände var följande:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">manage.py</span> <span class=\"pre\">test</span> <span class=\"pre\">polls</span></code> letade efter tester i applikationen <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code></p></li>\n<li><p>den hittade en underklass av <a class=\"reference internal\" href=\"/sv/5.2/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>-klassen</p></li>\n<li><p>skapade en särskild databas i syfte att testa</p></li>\n<li><p>den letade efter testmetoder - sådana vars namn börjar med <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code></p></li>\n<li><p>i <code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code> skapades en <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> instans vars <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> fält är 30 dagar i framtiden</p></li>\n<li><p>… och med hjälp av metoden <code class=\"docutils literal notranslate\"><span class=\"pre\">assertIs()</span></code> upptäckte den att dess <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> returnerar <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>, även om vi ville att den skulle returnera <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code></p></li>\n</ul>\n<p>Testet informerar oss om vilket test som misslyckades och till och med på vilken linje felet inträffade.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>Åtgärda felet<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>Vi vet redan vad problemet är: <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> ska returnera <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> om dess <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> ligger i framtiden. Ändra metoden i <code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>, så att den bara returnerar <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> om datumet också är i det förflutna:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/modeller.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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>och kör testet igen:</p>\n<div class=\"code-block\" data-language=\"pytb\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Pytb</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=\"Pytb code\"><code><span class=\"x\">Creating test database for alias &#39;default&#39;...</span>\n<span class=\"x\">System check identified no issues (0 silenced).</span>\n<span class=\"x\">.</span>\n<span class=\"x\">----------------------------------------------------------------------</span>\n<span class=\"x\">Ran 1 test in 0.001s</span>\n\n<span class=\"x\">OK</span>\n<span class=\"x\">Destroying test database for alias &#39;default&#39;...</span>\n</code></pre></div>\n<p>Efter att ha identifierat en bugg skrev vi ett test som avslöjar den och korrigerade buggen i koden så att vårt test passerade.</p>\n<p>Många andra saker kan gå fel med vår applikation i framtiden, men vi kan vara säkra på att vi inte oavsiktligt kommer att återinföra den här buggen, eftersom testet varnar oss omedelbart. Vi kan betrakta den här lilla delen av applikationen som säker för alltid.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>Mer omfattande tester<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>Medan vi är här kan vi ytterligare fastställa metoden <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>; det skulle faktiskt vara positivt pinsamt om vi genom att fixa en bugg hade introducerat en annan.</p>\n<p>Lägg till ytterligare två testmetoder i samma klass för att testa metodens beteende på ett mer heltäckande sätt:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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\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>Och nu har vi tre tester som bekräftar att <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> returnerar rimliga värden för tidigare, nyligen publicerade och framtida frågor.</p>\n<p>Återigen, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> är en minimal applikation, men hur komplex den än blir i framtiden och vilken annan kod den än interagerar med, har vi nu en viss garanti för att den metod vi har skrivit tester för kommer att bete sig på förväntade sätt.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>Testa en vy<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>Polls-programmet är ganska icke-diskriminerande: det kommer att publicera alla frågor, inklusive sådana vars <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>-fält ligger i framtiden. Vi bör förbättra detta. Att ange ett <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> i framtiden bör betyda att frågan publiceras vid den tidpunkten, men är osynlig fram till dess.</p>\n<section id=\"a-test-for-a-view\">\n<h3>Ett test för en vy<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>När vi åtgärdade buggen ovan skrev vi först testet och sedan koden för att åtgärda den. I själva verket var det ett exempel på testdriven utveckling, men det spelar egentligen ingen roll i vilken ordning vi gör arbetet.</p>\n<p>I vårt första test fokuserade vi noga på det interna beteendet i koden. I det här testet vill vi kontrollera beteendet så som det skulle upplevas av en användare via en webbläsare.</p>\n<p>Innan vi försöker fixa något, låt oss ta en titt på de verktyg vi har till vårt förfogande.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>Testklienten för Django<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 tillhandahåller ett test <a class=\"reference internal\" href=\"/sv/5.2/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> för att simulera en användare som interagerar med koden på vy-nivå.  Vi kan använda det i <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> eller till och med i <a class=\"reference internal\" href=\"/sv/5.2/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>Vi börjar igen med <a class=\"reference internal\" href=\"/sv/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>, där vi behöver göra ett par saker som inte kommer att vara nödvändiga i <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. Den första är att sätta upp testmiljön i <a class=\"reference internal\" href=\"/sv/5.2/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=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-2-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</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=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span>shell\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-2-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</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=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py shell\n</code></pre></div></div></div>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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=\"Python console 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=\"/sv/5.2/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> installerar en mallåtergivare som gör att vi kan undersöka några ytterligare attribut på svar som <code class=\"docutils literal notranslate\"><span class=\"pre\">response.context</span></code> som annars inte skulle vara tillgängliga. Observera att den här metoden <em>inte</em> sätter upp en testdatabas, så följande kommer att köras mot den befintliga databasen och resultatet kan skilja sig något beroende på vilka frågor du redan har skapat. Du kan få oväntade resultat om din <code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code> i <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code> inte är korrekt. Om du inte minns att du har ställt in den tidigare, kontrollera det innan du fortsätter.</p>\n<p>Därefter måste vi importera testklientklassen (senare i <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> kommer vi att använda <a class=\"reference internal\" href=\"/sv/5.2/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>-klassen, som levereras med sin egen klient, så detta kommer inte att behövas):</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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=\"Python console 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>När detta är klart kan vi be kunden att göra en del arbete åt oss:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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=\"Python console 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=\"s2\">&quot;/&quot;</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=\"s2\">&quot;polls:index&quot;</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;#x27;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=\"s2\">&quot;latest_question_list&quot;</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>Förbättra vår syn<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>Listan över opinionsundersökningar visar opinionsundersökningar som inte har publicerats ännu (dvs. de som har ett <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> i framtiden). Låt oss fixa det.</p>\n<p>I <a class=\"reference internal\" href=\"/sv/5.2/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a> introducerade vi en klassbaserad vy, baserad på <a class=\"reference internal\" href=\"/sv/5.2/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=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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=\"s2\">&quot;polls/index.html&quot;</span>\n    <span class=\"n\">context_object_name</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;latest_question_list&quot;</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=\"s2\">&quot;-pub_date&quot;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p>Vi måste ändra metoden <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> så att den även kontrollerar datumet genom att jämföra det med <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>. Först måste vi lägga till en import:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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>och sedan måste vi ändra metoden <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> så här:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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><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><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s2\">&quot;-pub_date&quot;</span><span class=\"p\">)[</span>\n        <span class=\"p\">:</span><span class=\"mi\">5</span>\n    <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> returnerar en frågeuppsättning som innehåller <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> vars <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> är mindre än eller lika med - det vill säga tidigare än eller lika med - <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>.</p>\n</section>\n<section id=\"testing-our-new-view\">\n<h3>Testar vår nya vy<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>Nu kan du försäkra dig om att det här fungerar som förväntat genom att starta <code class=\"docutils literal notranslate\"><span class=\"pre\">runserver</span></code>, ladda webbplatsen i din webbläsare, skapa några <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>-poster med datum i det förflutna och i framtiden, och kontrollera att endast de som har publicerats listas. Du vill inte behöva göra det <em>varenda gång du gör någon ändring som kan påverka detta</em> - så låt oss också skapa ett test, baserat på vår <a class=\"reference internal\" href=\"/sv/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>-session ovan.</p>\n<p>Lägg till följande i <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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>och vi kommer att skapa en genvägsfunktion för att skapa frågor samt en ny testklass:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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=\"s2\">&quot;polls:index&quot;</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=\"s2\">&quot;latest_question_list&quot;</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\">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=\"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=\"s2\">&quot;polls:index&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>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s2\">&quot;latest_question_list&quot;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"n\">question</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=\"s2\">&quot;polls:index&quot;</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=\"s2\">&quot;latest_question_list&quot;</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\">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=\"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=\"s2\">&quot;polls:index&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>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s2\">&quot;latest_question_list&quot;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"n\">question</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\">question1</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=\"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\">question2</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=\"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=\"s2\">&quot;polls:index&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>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s2\">&quot;latest_question_list&quot;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"n\">question2</span><span class=\"p\">,</span> <span class=\"n\">question1</span><span class=\"p\">],</span>\n        <span class=\"p\">)</span>\n</code></pre></figure>\n<p>Låt oss titta närmare på några av dessa.</p>\n<p>Den första är en genvägsfunktion för frågor, <code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>, som gör att processen för att skapa frågor inte behöver upprepas.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code> skapar inte några frågor, men kontrollerar meddelandet: ”Inga omröstningar är tillgängliga.” och verifierar att <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> är tom. Observera att klassen <a class=\"reference internal\" href=\"/sv/5.2/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> tillhandahåller ytterligare några assertion-metoder. I dessa exempel använder vi <a class=\"reference internal\" href=\"/sv/5.2/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> och <a class=\"reference internal\" href=\"/sv/5.2/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>I <code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code> skapar vi en fråga och kontrollerar att den visas i listan.</p>\n<p>I <code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code> skapar vi en fråga med ett <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> i framtiden. Databasen återställs för varje testmetod, så den första frågan finns inte längre kvar, och indexet bör inte heller ha några frågor i sig.</p>\n<p>Och så vidare. I själva verket använder vi testerna för att berätta en historia om administratörsinmatning och användarupplevelse på webbplatsen, och kontrollerar att de förväntade resultaten publiceras vid varje tillstånd och för varje ny förändring i systemets tillstånd.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3>Testning av <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>Det vi har fungerar bra, men även om framtida frågor inte visas i <em>index</em> kan användarna fortfarande nå dem om de känner till eller gissar rätt URL. Så vi måste lägga till en liknande begränsning till <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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\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>Vi bör sedan lägga till några tester för att kontrollera att en ”fråga” vars ”publiceringsdatum” ligger i det förflutna kan visas, och att en fråga med ett ”publiceringsdatum” i framtiden inte kan visas:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</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=\"Python code\"><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=\"s2\">&quot;Future question.&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\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s2\">&quot;polls:detail&quot;</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=\"s2\">&quot;Past Question.&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\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s2\">&quot;polls:detail&quot;</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>Idéer för fler tester<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>Vi borde lägga till en liknande <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code>-metod till <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code> och skapa en ny testklass för den vyn. Den kommer att vara mycket lik det vi just har skapat; i själva verket kommer det att finnas en hel del upprepningar.</p>\n<p>Vi kan också förbättra vår applikation på andra sätt och lägga till tester längs vägen. Det är till exempel meningslöst att en <code class=\"docutils literal notranslate\"><span class=\"pre\">Fråga</span></code> utan relaterad <code class=\"docutils literal notranslate\"><span class=\"pre\">Val</span></code> kan publiceras på webbplatsen. Så våra vyer kan kontrollera detta och utesluta sådana <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>-objekt. Våra tester skulle skapa en <code class=\"docutils literal notranslate\"><span class=\"pre\">Fråga</span></code> utan en <code class=\"docutils literal notranslate\"><span class=\"pre\">Val</span></code>, och sedan testa att den inte publiceras, samt skapa en liknande <code class=\"docutils literal notranslate\"><span class=\"pre\">Fråga</span></code> * med * minst en <code class=\"docutils literal notranslate\"><span class=\"pre\">Val</span></code>, och testa att den * är * publicerad.</p>\n<p>Kanske bör inloggade administratörsanvändare tillåtas se opublicerade ”frågor”, men inte vanliga besökare. Återigen: vad som än behöver läggas till i programvaran för att åstadkomma detta bör åtföljas av ett test, oavsett om du skriver testet först och sedan får koden att klara testet, eller om du först utarbetar logiken i din kod och sedan skriver ett test för att bevisa det.</p>\n<p>Vid en viss tidpunkt måste du titta på dina tester och undra om din kod lider av testbloat, vilket leder oss till:</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>Vid testning är mer bättre<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>Det kan tyckas att våra tester håller på att växa utom kontroll. I den här takten kommer det snart att finnas mer kod i våra tester än i vår applikation, och upprepningen är oestetisk jämfört med den eleganta koncisa resten av vår kod.</p>\n<p><strong>Det spelar ingen roll</strong>. Låt dem växa. För det mesta kan du skriva ett test en gång och sedan glömma bort det. Det kommer att fortsätta att utföra sin användbara funktion när du fortsätter att utveckla ditt program.</p>\n<p>Ibland behöver tester uppdateras. Anta att vi ändrar våra vyer så att endast <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>-poster med tillhörande <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code>-instanser publiceras. I det fallet kommer många av våra befintliga tester att misslyckas - <em>teller oss exakt vilka tester som behöver ändras för att få dem uppdaterade</em>, så i den utsträckningen hjälper testerna till att ta hand om sig själva.</p>\n<p>I värsta fall, när du fortsätter att utveckla, kan du upptäcka att du har några tester som nu är överflödiga. Inte ens det är ett problem; när det gäller testning är redundans en <em>bra</em> sak.</p>\n<p>Så länge dina tester är förnuftigt arrangerade kommer de inte att bli ohanterliga. Bra tumregler inkluderar att ha:</p>\n<ul class=\"simple\">\n<li><p>en separat <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code> för varje modell eller vy</p></li>\n<li><p>en separat testmetod för varje uppsättning villkor som du vill testa</p></li>\n<li><p>namn på testmetoder som beskriver deras funktion</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>Ytterligare tester<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>Denna handledning introducerar bara några av grunderna i testning. Det finns mycket mer du kan göra, och ett antal mycket användbara verktyg till ditt förfogande för att uppnå några mycket smarta saker.</p>\n<p>Till exempel, medan våra tester här har täckt en del av den interna logiken i en modell och hur våra vyer publicerar information, kan du använda ett ”in-browser”-ramverk som <a class=\"reference external\" href=\"https://www.selenium.dev/\">Selenium</a> för att testa hur din HTML faktiskt återges i en webbläsare. Med dessa verktyg kan du inte bara kontrollera beteendet hos din Django-kod, utan också till exempel hos ditt JavaScript. Det är något alldeles extra att se testerna starta en webbläsare och börja interagera med din webbplats, som om en människa körde den! Django inkluderar <a class=\"reference internal\" href=\"/sv/5.2/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> för att underlätta integration med verktyg som Selenium.</p>\n<p>Om du har en komplex applikation kanske du vill köra tester automatiskt med varje commit för ”kontinuerlig integration”, så att kvalitetskontrollen i sig själv - åtminstone delvis - är automatiserad.</p>\n<p>Ett bra sätt att upptäcka otestade delar av din applikation är att kontrollera kodtäckningen. Detta hjälper också till att identifiera bräcklig eller till och med död kod. Om du inte kan testa en del av koden betyder det vanligtvis att koden bör omarbetas eller tas bort. Täckning hjälper till att identifiera död kod. Se <a class=\"reference internal\" href=\"/sv/5.2/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Integration med coverage.py</span></a> för mer information.</p>\n<p><a class=\"reference internal\" href=\"/sv/5.2/topics/testing/\"><span class=\"doc\">Testing in Django</span></a> har omfattande information om testning.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>Vad kommer härnäst?<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>För fullständig information om testning, se <a class=\"reference internal\" href=\"/sv/5.2/topics/testing/\"><span class=\"doc\">Testning i Django</span></a>.</p>\n<p>När du känner dig bekväm med att testa Django-vyer kan du läsa <a class=\"reference internal\" href=\"/sv/5.2/intro/tutorial06/\"><span class=\"doc\">del 6 av denna handledning</span></a> för att lära dig mer om hantering av statiska filer.</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"Introduktion av automatiserad testning","anchor":"introducing-automated-testing","children":[{"title":"Vad är automatiserade tester?","anchor":"what-are-automated-tests","children":[]},{"title":"Varför du behöver skapa tester","anchor":"why-you-need-to-create-tests","children":[{"title":"Tester sparar tid","anchor":"tests-will-save-you-time","children":[]},{"title":"Tester identifierar inte bara problem, de förebygger dem","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"Tester gör din kod mer attraktiv","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"Tester hjälper team att arbeta tillsammans","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"Grundläggande teststrategier","anchor":"basic-testing-strategies","children":[]},{"title":"Skriva vårt första test","anchor":"writing-our-first-test","children":[{"title":"Vi identifierar ett fel","anchor":"we-identify-a-bug","children":[]},{"title":"Skapa ett test för att avslöja felet","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"Genomföra tester","anchor":"running-tests","children":[]},{"title":"Åtgärda felet","anchor":"fixing-the-bug","children":[]},{"title":"Mer omfattande tester","anchor":"more-comprehensive-tests","children":[]}]},{"title":"Testa en vy","anchor":"test-a-view","children":[{"title":"Ett test för en vy","anchor":"a-test-for-a-view","children":[]},{"title":"Testklienten för Django","anchor":"the-django-test-client","children":[]},{"title":"Förbättra vår syn","anchor":"improving-our-view","children":[]},{"title":"Testar vår nya vy","anchor":"testing-our-new-view","children":[]},{"title":"Testning av DetailView","anchor":"testing-the-detailview","children":[]},{"title":"Idéer för fler tester","anchor":"ideas-for-more-tests","children":[]}]},{"title":"Vid testning är mer bättre","anchor":"when-testing-more-is-better","children":[]},{"title":"Ytterligare tester","anchor":"further-testing","children":[]},{"title":"Vad kommer härnäst?","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"Kom igång","url":"/sv/5.2/intro/"}],"prev":{"docname":"intro/tutorial04","title":"Skriva din första Django-app, del 4","url":"/sv/5.2/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"Skriva din första Django-app, del 6","url":"/sv/5.2/intro/tutorial06/"},"formats":{"html":"/sv/5.2/intro/tutorial05/","markdown":"/sv/5.2/intro/tutorial05.md","json":"/sv/5.2/intro/tutorial05.json"},"source":"https://github.com/django/django/blob/stable/5.2.x/docs/intro/tutorial05.txt","official":"https://docs.djangoproject.com/sv/5.2/intro/tutorial05/","inVersions":["6.1","6.0","5.2"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}