{"title":"Pisanie pierwszej aplikacji Django, część 5.","version":"5.2","locale":"pl","docname":"intro/tutorial05","url":"/pl/5.2/intro/tutorial05/","canonical":"https://djangodocs.dev/pl/5.2/intro/tutorial05/","summary":"Ten tutorial zaczyna się, gdzie skończył się Tutorial 4 . Zbudowaliśmy aplikację web-ankietową, teraz stworzymy dla niej kilka zautomatyzowanych testów. Gdzie…","html":"<h1>Pisanie pierwszej aplikacji Django, część 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>Ten tutorial zaczyna się, gdzie skończył się <a class=\"reference internal\" href=\"/pl/5.2/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a>. Zbudowaliśmy aplikację web-ankietową, teraz stworzymy dla niej kilka zautomatyzowanych testów.</p>\n<aside class=\"admonition-where-to-get-help admonition\">\n<p class=\"admonition-title\">Gdzie szukać pomocy:</p>\n<p>Jeśli masz trudności w przejściu tego tutorialu, przejdź do sekcji <a class=\"reference internal\" href=\"/pl/5.2/faq/help/\"><span class=\"doc\">Uzyskiwanie pomocy</span></a> często zadawanych pytań.</p>\n</aside>\n<section id=\"introducing-automated-testing\">\n<h2>Wprowadzenie do zautomatyzowanych testów<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>Czym są zautomatyzowane testy?<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>Testy są rutynami, które sprawdzają działanie twojego kodu.</p>\n<p>Testowanie działa na różnych poziomach. Niektóre testy mogą mieć zastosowanie do małych detali (<em>czy szczególna metoda modelu zwraca spodziewane wartości?</em>), podczas gdy inne sprawdzają ogólne działanie software’u (<em>czy sekwecja wejścia od użytkownika na stronie daje pożądany rezultat?</em>). Nie różni się to niczym od rodzaju testów, które robiłeś wcześniej w <a class=\"reference internal\" href=\"/pl/5.2/intro/tutorial02/\"><span class=\"doc\">Tutorialu 2</span></a>, używając <a class=\"reference internal\" href=\"/pl/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>, by sprawdzić zachowanie metody, lub uruchamiając aplikację i wprowadzające dane, by sprawdzić, jak się zachowuje.</p>\n<p>To, co jest innego w testach <em>zautomatyzowanych</em>, to to, że testowanie wykonywanie jest dla ciebie przez system. Raz tworzysz zestaw testów i później, kiedy robisz zmiany w swojej aplikacji, możesz sprawdzić, czy twój kod nadal działa jak to pierwotnie zamierzałeś, bez konieczności wykonywania pochłaniających czas testów ręcznych.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>Dlaczego potrzebujesz tworzyć testy<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>Więc dlatego tworzyć testy i dlaczego teraz?</p>\n<p>Możesz mieć wrażenie, że masz wystarczająco dużo na talerzu ucząc się tylko Pythona/Django i mając znów kolejną rzecz do nauki może wydawać się przytłaczające i może niepotrzebne. Ostatecznie nasza aplikacja ankietowa pracuje teraz całkiem szczęśliwie; przechodzenie przez problem tworzenia zautomatyzowanych testów nie spowoduje, że będzie działać choć trochę lepiej. Jeśli tworzenie aplikacji ankietowej jest ostatnim programowaniem w Django, jaki kiedykolwiek zrobisz, wtedy prawda, nie potrzebujesz wiedzieć, jak robić zautomatyzowane testy. Ale jeśli to nie jest twój przypadek, teraz jest wspaniały czas do nauki.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>Testy oszczędzą ci czas<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>Do pewnego momentu, „sprawdzenie czy zdaje się działać” będzie satysfakcjonującym testem. W bardziej złożonej aplikacji możesz mieć tuziny złożonych interakcji pomiędzy komponentami.</p>\n<p>Zmiana w jakimkolwiek z tych komponentów może mieć nieprzewidziane konsekwencje w zachowaniu aplikacji. Sprawdzenie, czy nadal „zdaje się działać” może znaczyć przechodzenie przez całą funkcjonalność twojego kodu z dwudziestoma różnymi wariacjami danych testowych, by upewnić się, czy czegoś nie zepsułeś – nie jest to dobrze wykorzystany czas.</p>\n<p>Jest to szczególnie prawdą kiedy zautomatyzowane testy mogą zrobić to za ciebie w sekundy. Jeśli coś poszło źle, testy pomogą też w zidentyfikowaniu kodu, który powoduje nieprzewidziane zachowanie.</p>\n<p>Czasem może wydawać się żmudne odrywać się od produktywnej, kreatywnej pracy programistycznej, aby zmierzyć się z szarym i nie ekscytującym biznesem pisania testów, szczególnie kiedy wiesz, że twój kod działa dobrze.</p>\n<p>Jednakże zadanie pisania testów jest o wiele bardziej satysfakcjonujące niż spędzanie godzin na testowaniu ręcznie swojej aplikacji lub na próbach identyfikacji przyczyny nowo-wprowadzonego problemu.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>Testy nie tylko identyfikują problemy, one pomagają ich unikać<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>Błędem jest myśleć o testach głównie jako negatywnym aspekcie dewelopmentu.</p>\n<p>Bez testów cel lub zamierzone działanie aplikacji może być raczej nieprzejrzyste. Nawet jeśli to twój własny kod, czasem okaże się, że czasem w nim węszysz próbując odkryć co dokładnie robi.</p>\n<p>Testy to zmieniają; oświetlają twój kod z wnętrza i kiedy coś pójdzie nie tak, skupiają światłona części, która poszła źle – <em>nawet kiedy się nie zorientowałeś, że poszło źle</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>Testy powodują, że twój kod jest bardziej atrakcyjny<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>Mógłbyś stworzyć wspaniały kawałek kodu, ale okaże się, że wielu deweloperów odmówi spojrzenia na niego, bo brakuje mu testów; bez testów, nie będą mu ufać. Jacob Kaplan-Moss, jeden z pierwotnych twórców Django mówi: „Kod bez testów jest popsuty z założenia”.</p>\n<p>To, że inni deweloperzy chcą mieć testy w twoim oprogramowaniu zanim potraktują go na poważnie jest kolejnym powodem, byś zaczął pisać testy.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>Testy pomagają zespołom pracować razem<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>Poprzednie punkty są napisane z perspektywy pojedynczego dewelopera opiekującego się aplikacją. Złożone aplikacje będą utrzymywane przez zespoły. Testy gwarantują, że koledzy nie zepsują przypadkowo twojego kodu (i że ty nie zepsujesz ich bez wiedzy o tym). Jeśli chcesz utrzymywać się jako programista Django, musisz być dobry w pisaniu testów!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>Podstawowe strategie testowania<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>Jest wiele sposobów, w jaki można podejść do pisania testów.</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\ncounterintuitive, 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 formalizes the problem in a Python test case.</p>\n<p>Częściej ktoś nowy w testowanie stworzy pewien kod i później zdecyduje, że powinien on mieć jakieś testy. Prawdopodobnie lepiej byłoby napisać część testów wcześniej, ale nigdy nie jest za późno, żeby zacząć to robić.</p>\n<p>Czasem trudno jest wymyślić, od czego zacząć w pisaniu testów. Jeśli napisałeś kilka tysięcy linii Pythona, wybranie czegoś do przetestowania może nie być łatwe. W takim przypadku owocnym jest napisać pierwszy test następnym razem, gdy wprowadzisz zmianę, czy to dodajesz nową funkcjonalność, czy naprawiasz błąd.</p>\n<p>A więc zróbmy to teraz.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>Pisanie naszego pierwszego testu<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>Odnajdujemy błąd<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>Na szczęście w aplikacji <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> jest mały błąd do naprawienia przez nas od razu: metoda <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> zwraca <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> jeśli <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> zostało opublikowane w ostatnim dniu (co jest poprawne) ale również jeśli pole <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> instancji <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> jest w przyszłości (co poprawne z pewnością nie jest).</p>\n<p>Potwierdź buga używając <a class=\"reference internal\" href=\"/pl/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shella</span></code></a> i sprawdź metodę na pytaniu, którego data jest w przyszłości:</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>Ponieważ rzeczy w przyszłości nie są „recent”, to jest oczywiście źle.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>Stwórz test, aby wskazać błąd<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>To, co zrobiliśmy w :djadmin:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>shell`u, aby sprawdzić obecność problemu jest dokładnie tym, co chcemy zrobić w zautomatyzowanym teście, więc obróćmy to w zautomatyzowany test.</p>\n<p>Miejscem według konwencji na testy aplikacji jest plik <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> aplikacji; system testowania automatycznie znajdzie testy w każdym pliku, którego nazwa zaczyna się od <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>.</p>\n<p>Wprowadź poniższe w plik <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> aplikacji <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>Stworzyliśmy tutaj podklasę <a class=\"reference internal\" href=\"/pl/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> z metodą, która tworzy instancję <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> z <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> w przyszłości. Następnie sprawdzamy wyjście <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> – które <em>powinno</em> być False.</p>\n</section>\n<section id=\"running-tests\">\n<h3>Uruchomienie testów<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>W terminalu możemy uruchomić nasz 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>i zobaczysz coś takiego:</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\">Inny błąd?</p>\n<p>Jeśli dostajesz tutaj <code class=\"docutils literal notranslate\"><span class=\"pre\">NameError</span></code>, być może ominąłeś krok w <a class=\"reference internal\" href=\"/pl/5.2/intro/tutorial02/#tutorial02-import-timezone\"><span class=\"std std-ref\">części 2</span></a>, gdzie dodaliśmy importy <code class=\"docutils literal notranslate\"><span class=\"pre\">datetime</span></code> i <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone</span></code> do <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/models.py</span></code>. Skopiuj importy z tamtej sekcji i spróbuj uruchomić testy ponownie.</p>\n</aside>\n<p>To, co się stało, to to:</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> wyszukał testy w aplikacji <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code></p></li>\n<li><p>znalazł podklasę klasy <a class=\"reference internal\" href=\"/pl/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></p></li>\n<li><p>stworzył specjalną bazę danych na potrzebę testowania</p></li>\n<li><p>wyszukał metody testowe – te, których nazwy zaczynają się od <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code></p></li>\n<li><p>w <code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code> stworzył instancję <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>, której pole <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> jest 30 dni w przyszłości</p></li>\n<li><p>…i używając metody <code class=\"docutils literal notranslate\"><span class=\"pre\">assertls()</span></code> odkrył, że jej <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> zwraca <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>, chociaż chcieliśmy, aby zwracała <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code></p></li>\n</ul>\n<p>Test informuje nas który test się nie powiódł i nawet w której linii ukazał się błąd.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>Naprawianie błędu<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>Wiemy już, co jest problemem: <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> powinna zwracać <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> jeśli jej <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> jest w przyszłości. Zmień metodę w <code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>, aby zwracała <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> tylko jeśli data jest też w przeszłości:</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/models.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>i uruchom test ponownie:</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>Po zidentyfikowaniu buga, napisaliśmy test, który go pokazuje i poprawiliśmy błąd w kodzie tak, aby test przechodził.</p>\n<p>Wiele innych rzeczy może pójść źle z naszą aplikacją w przyszłości, ale możemy być pewni, że nie zreintrodukujemy przypadkowo tego błędu, ponieważ uruchomienie testu błyskawicznie nas ostrzeże. Możemy traktować tę małą porcję aplikacji na zawsze jako bezpieczną.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>Obszerniejsze testy<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>Kiedy tu jesteśmy, możemy dalej przyprzeć do muru metodę <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>; w zasadzie byłoby pozytywnie zawstydzające jeśli naprawiając jeden błąd wprowadziliśmy inny.</p>\n<p>Dodaj dwie metody testowe więcej do tej samej klasy, aby przetestować zachowanie metody bardziej wszechstronnie:</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>I mamy teraz trzy testy, które potwierdzają, że <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> zwraca sensowne wartości dla przeszłych, ostatnich i przyszłych pytań.</p>\n<p>Znowu, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> jest małą aplikacją, ale jakkolwiek złożona będzie w przyszłości i z jakimkolwiek kodem będzie w interakcji, mamy teraz pewną gwarancję, że metoda, dla której napisaliśmy testy będzie zachowywała się w przewidywalne sposoby.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>Przetestuj widok<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>Aplikacja ankietowa jest bardzo niedyskryminująca: opublikuje każde pytanie, włącznie z takimi, których pole <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> wskazuje na datę w przyszłości. Powinniśmy to poprawić. Ustawienie <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> w przyszłości powinno oznaczać, że Question jest publikowane dopiero w tym przyszłym momencie i niewidoczne do tego czasu.</p>\n<section id=\"a-test-for-a-view\">\n<h3>Test dla widoku<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>Kiedy naprawiliśmy powyżej błąd, napisaliśmy najpierw test i później kod, który naprawił błąd. Faktycznie to był przykład test-driven dewelopmentu, ale to nie ma wielkiego znaczenia, w jakiej kolejności wykonujemy tę pracę.</p>\n<p>W naszym pierwszym teście skupiliśmy się bardzo na wewnętrznym zachowaniu kodu. W tym teście chcemy sprawdzić jego działanie tak, jak byłoby obserwowane przez użytkownika przez przeglądarkę internetową.</p>\n<p>Zanim spróbujemy cokolwiek naprawić, spójrzmy na narzędzia, którymi dysponujemy.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>Klient testowy 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 dostarcza testowe <a class=\"reference internal\" href=\"/pl/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> do symulacji interakcji użytkownika z kodem na poziomie widoku. Możemy go użyć w <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> lub nawet w <a class=\"reference internal\" href=\"/pl/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shellu</span></code></a>.</p>\n<p>Zaczniemy znowu w <a class=\"reference internal\" href=\"/pl/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shellu</span></code></a>, gdzie musimy zrobić parę rzeczy, które nie będą koniczne w <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. Pierwsze, to uruchomić środowisko testowe w <a class=\"reference internal\" href=\"/pl/5.2/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shellu</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=\"/pl/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> instaluje renderer szablonów, który pozwala nam sprawdzać niektóre dodatkowe atrybuty odpowiedzi, takie jak <code class=\"docutils literal notranslate\"><span class=\"pre\">response.context</span></code>, które inaczej nie byłyby dostępne. Miej na uwadze, że ta metoda <em>nie</em> uruchamia testowej bazy danych, więc poniższe będzie uruchamiane na istniejącej bazie danych i wynik może nieco się różnić w zależności od pytań, które dotychczas stworzyłeś. Możesz otrzymać nieprzewidywane wyniki, jeśli twoje <code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code> w <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code> nie jest poprawne. Jeśli nie pamiętasz, abyś je wcześniej ustawiał, sprawdź zanim kontynuujesz.</p>\n<p>Następnie musimy zaimportować klasę klienta testowego (poźniej w <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> będziemy używać klasy <a class=\"reference internal\" href=\"/pl/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>, która ma swojego własnego klienta, więc ten nie będzie wymagany):</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>Z zaimportowaną klasą, możemy zlecić klientowi wykonanie dla nas pracy:</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>Udoskonalanie naszego widoku<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>Lista ankiet pokazuje ankiety, które nie są jeszcze opublikowane (te, które mają <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> w przyszłości). Naprawmy to.</p>\n<p>w <a class=\"reference internal\" href=\"/pl/5.2/intro/tutorial04/\"><span class=\"doc\">Tutorialu 4</span></a> wprowadziliśmy class-based view, oparty na <a class=\"reference internal\" href=\"/pl/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>Musimy zmienić metodę <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> tak, aby sprawdzała też datę porównując ją z <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>. Najpierw musimy dodać 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>i później zmienić metodę <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> w ten sposób:</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> 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>Testowanie naszego nowego widoku<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\n<code class=\"docutils literal notranslate\"><span class=\"pre\">runserver</span></code>, loading the site in your browser, creating a few <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>\nentries with dates in the past and future, and checking that only those that\nhave been published are listed. You don’t want to have to do that <em>every single\ntime you make any change that might affect this</em> - so let’s also create a test,\nbased on our <a class=\"reference internal\" href=\"/pl/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 above.</p>\n<p>Dodaj poniższe do <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>i stworzymy funkcję skrótową do tworzenia pytań oraz nową klasę testową:</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>Przyjmy się dokładniej niektórym z nich.</p>\n<p>PIerwsze to funkcja skrótowa pytań, <code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>, wyciąga powtórzenia z procesu tworzenia pytań.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code> nie tworzy żadnych pytań, ale sprawdza komunikat „No polls are available.” i weryfikuje, czy <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> jest puste. Zwróć uwagę, że <a class=\"reference internal\" href=\"/pl/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> dostarcza kilka dodatkowych metod asercji. W tych przypadkach używamy <a class=\"reference internal\" href=\"/pl/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> i <a class=\"reference internal\" href=\"/pl/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>W <code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code> tworzymy pytanie i weryfikujemy, że pojawia się na liście.</p>\n<p>W <code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code> tworzymy pytanie z <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> w przyszłości. Baza danych jest resetowana dla każdej testowej metody, więc pierwszego pytania już tutaj nie ma i znów indeks nie powinien zawierać żadnego pytania.</p>\n<p>I tak dalej. W efekcie używamy testów, aby opowiedzieć historię o danych wprowadzonych przez administratora i doświadczeniu użytkownika na stronie i sprawdzamy na każdym etapie i dla każdej nowej zmiany w stanie systemu, czy publikowane są oczekiwane rezultaty.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3>Testowanie <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>To co mamy działa dobrze, jednakże mimo to, że przyszłe pytania nie pojawiają się w <em>indeksie</em>, użytkownicy wciąż mogą uzyskać do nich dostęp jeśli znają lub zgadną odpowiedni URL. Musimy więc dodać podobny warunek do <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>Następnie powinniśmy dodać kilka testów, aby sprawdzić że <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>, którego <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> jest w przeszłości może zostać wyświetlone i takie z <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> w przyszłości nie może:</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>Pomysły na więcej testów<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>Powinniśmy dodać podobną metodę <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> do <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code> i stworzyć nową klasę testową dla tego widoku. Będzie bardzo podobna do tego, co przed chwilą stworzyliśmy; tak naprawdę będzie bardzo dużo powtórzone.</p>\n<p>We could also improve our application in other ways, adding tests along the\nway. For example, it’s pointless that a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> with no related <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code>\ncan be published on the site. So, our views could check for this, and exclude\nsuch <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> objects. Our tests would create a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> without a\n<code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code>, and then test that it’s not published, as well as create a similar\n<code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> <em>with</em> at least one <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code>, and test that it <em>is</em> published.</p>\n<p>Perhaps logged-in admin users should be allowed to see unpublished <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>\nentries, 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>W pewnym momencie na pewno spojrzysz na swoje testy i zaczniesz zastanawiać się, czy Twój kod nie cierpi na testowe wzdęcia, co prowadzi nas do:</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>Testując, im więcej tym lepiej<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>Może się wydawać, że nasze testy zaczynają wymykać się spod kontroli. W takim tempie wkrótce będzie więcej kodu w naszych testach niż w naszej aplikacji, a powtórzenia są nieestetyczne, w porównaniu do eleganckiej zwięzłości reszty naszego kodu.</p>\n<p><strong>To nie ma znaczenia</strong>. Pozwól im rosnąć. W większości przypadków możesz napisać test raz i później zapomnieć o nim. Będzie dalej wykazywał przydatność w trakcie twojej pracy nad programem.</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\">Question</span></code> entries with associated <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code> instances are published.\nIn that case, many of our existing tests will fail - <em>telling us exactly which\ntests need to be amended to bring them up to date</em>, so to that extent tests\nhelp look after themselves.</p>\n<p>W najgorszym przypadku w trakcie pracy możesz zorientować się, że masz jakieś testy, które są teraz redundantne. Nawet to nie jest problemem; w testowaniu redundancja jest rzeczą <em>dobrą</em>.</p>\n<p>Tak długo jak twoje testy są sensownie ułożone, nie zaczną być nie do zarządzania. Na praktyczne zasady składa się posiadanie:</p>\n<ul class=\"simple\">\n<li><p>oddzielnego <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code> dla każdego modelu lub widoku</p></li>\n<li><p>oddzielnej metody testowej dla każdego zbioru warunków, które chcesz sprawdzać</p></li>\n<li><p>nazw metod testowych, które opisują swoje funkcje</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>Dalsze testowanie<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>Ten tutorial przedstawia tylko podstawy testowania. Jest bardzo wiele rzeczy, które moesz robić i parę bardzo przydatnych narzędzi do twojej dyspozycji, by osiągać sprytne rzeczy.</p>\n<p>Na przykład, podczas gdy nasze testy tutaj pokryły część wewnętrznej logiki modelu i sposobu, w jaki nasze widoki publikują informacje, możesz użyć „przeglądarkowego” frameworku takiego jak <a class=\"reference external\" href=\"https://www.selenium.dev/\">Selenium</a>, by testować, w jaki sposób twój HTML tak naprawdę renderuje się w przeglądarce. Takie narzędzia pozwalają na sprawdzenie nie tylko zachowania twojego kodu Django, ale też na przykład twojego JavaScriptu. Jest coś w tym, widzieć testy odpalane w przeglądarce i jak wchodzą w interakcje z twoją stroną, tak jakby robił to człowiek! Django zawiera <a class=\"reference internal\" href=\"/pl/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>, aby ułatwić integrację z narzędziami jak Selenium.</p>\n<p>Jeśli masz złożoną aplikację, możesz chcieć uruchamiać testy automatycznie z każdym commitem w celu utrzymania <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Continuous_integration\">ciągłej integracji</a>, aby kontrola jakości była – przynajmniej częściowo – zautomatyzowana.</p>\n<p>Dobrym sposobem, aby dostrzec nietestowane części twojej aplikacji, jest sprawdzić pokrycie testami. To pomaga też znaleźć delikatny lub nawet martwy kod. Jeśli nie możesz przetestować kawałka kodu, zazwyczaj znaczy to, że powinien on zostać zrefaktorowany lub usunięty. Pokrycie pomoże ci znaleźć martwy kod. Zobacz <a class=\"reference internal\" href=\"/pl/5.2/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Integration with coverage.py</span></a> po szczegóły.</p>\n<p><a class=\"reference internal\" href=\"/pl/5.2/topics/testing/\"><span class=\"doc\">Testowanie w Django</span></a> zawiera obszerną informację na temat testowania.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>Co dalej?<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>Po pełną informację na temat testowania, zobacz <a class=\"reference internal\" href=\"/pl/5.2/topics/testing/\"><span class=\"doc\">Testowanie w Django</span></a>.</p>\n<p>Kiedy już czujesz się dobrze z testowaniem widoków Django, przeczytaj <a class=\"reference internal\" href=\"/pl/5.2/intro/tutorial06/\"><span class=\"doc\">część 6 tego tutoriala</span></a>, aby nauczyć się zarządzania plikami statycznymi.</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"Wprowadzenie do zautomatyzowanych testów","anchor":"introducing-automated-testing","children":[{"title":"Czym są zautomatyzowane testy?","anchor":"what-are-automated-tests","children":[]},{"title":"Dlaczego potrzebujesz tworzyć testy","anchor":"why-you-need-to-create-tests","children":[{"title":"Testy oszczędzą ci czas","anchor":"tests-will-save-you-time","children":[]},{"title":"Testy nie tylko identyfikują problemy, one pomagają ich unikać","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"Testy powodują, że twój kod jest bardziej atrakcyjny","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"Testy pomagają zespołom pracować razem","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"Podstawowe strategie testowania","anchor":"basic-testing-strategies","children":[]},{"title":"Pisanie naszego pierwszego testu","anchor":"writing-our-first-test","children":[{"title":"Odnajdujemy błąd","anchor":"we-identify-a-bug","children":[]},{"title":"Stwórz test, aby wskazać błąd","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"Uruchomienie testów","anchor":"running-tests","children":[]},{"title":"Naprawianie błędu","anchor":"fixing-the-bug","children":[]},{"title":"Obszerniejsze testy","anchor":"more-comprehensive-tests","children":[]}]},{"title":"Przetestuj widok","anchor":"test-a-view","children":[{"title":"Test dla widoku","anchor":"a-test-for-a-view","children":[]},{"title":"Klient testowy Django","anchor":"the-django-test-client","children":[]},{"title":"Udoskonalanie naszego widoku","anchor":"improving-our-view","children":[]},{"title":"Testowanie naszego nowego widoku","anchor":"testing-our-new-view","children":[]},{"title":"Testowanie DetailView","anchor":"testing-the-detailview","children":[]},{"title":"Pomysły na więcej testów","anchor":"ideas-for-more-tests","children":[]}]},{"title":"Testując, im więcej tym lepiej","anchor":"when-testing-more-is-better","children":[]},{"title":"Dalsze testowanie","anchor":"further-testing","children":[]},{"title":"Co dalej?","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"Pierwsze kroki","url":"/pl/5.2/intro/"}],"prev":{"docname":"intro/tutorial04","title":"Pisanie pierwszej aplikacji Django, część 4.","url":"/pl/5.2/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"Pisanie pierwszej aplikacji Django, część 6.","url":"/pl/5.2/intro/tutorial06/"},"formats":{"html":"/pl/5.2/intro/tutorial05/","markdown":"/pl/5.2/intro/tutorial05.md","json":"/pl/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/pl/5.2/intro/tutorial05/","inVersions":["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"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}