{"title":"첫 번째 장고 앱 작성하기, part 5","version":"2.1","locale":"ko","docname":"intro/tutorial05","url":"/ko/2.1/intro/tutorial05/","canonical":"https://djangodocs.dev/ko/2.1/intro/tutorial05/","summary":"이 튜토리얼은 튜토리얼 4장 에서 이어집니다. 우리는 웹 설문조사 애플리케이션을 구축했으며 이제 이를 위한 자동화된 테스트를 작성할 것입니다. 자동화된 테스트 소개 Link to this heading # 자동화된 테스트란 무엇입니까? Link to this heading # 테스트는 코드의…","html":"<h1>첫 번째 장고 앱 작성하기, part 5<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-5\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>이 튜토리얼은 <a class=\"reference internal\" href=\"/ko/2.1/intro/tutorial04/\"><span class=\"doc\">튜토리얼 4장</span></a>에서 이어집니다. 우리는 웹 설문조사 애플리케이션을 구축했으며 이제 이를 위한 자동화된 테스트를 작성할 것입니다.</p>\n<section id=\"introducing-automated-testing\">\n<h2>자동화된 테스트 소개<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>자동화된 테스트란 무엇입니까?<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>테스트는 코드의 동작을 체크하는 간단한 일련의 작업들입니다.</p>\n<p>테스트는 다양한 수준에서 작동합니다. 일부 테스트는 작은 세부 사항에 적용될 수 있습니다 (<em>특정 모델 메서드는 예상대로 값을 반환합니까?</em>) 또 다른 테스트는 소프트웨어의 전반적인 작동을 검사합니다 (<em>사이트에서 사용자 입력 시퀀스가 원하는 결과를 생성합니까?</em>). 이것은 이전 <a class=\"reference internal\" href=\"/ko/2.1/intro/tutorial02/\"><span class=\"doc\">튜토리얼 2장</span></a>에서 <a class=\"reference internal\" href=\"/ko/2.1/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><em>자동화 된</em> 테스트에서 다른 점은 테스트 작업이 시스템에서 수행된다는 것입니다. 한 번 테스트 세트를 작성한 이후에는 앱을 변경할 때 수동 테스트를 수행하지 않아도 원래 의도대로 코드가 작동하는지 확인할 수 있습니다.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>테스트를 만들어야하는 이유<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>그래서 왜 테스트를 만들어야하고, 굳이 지금 해야할까요?</p>\n<p>아마 당신은 Python/Django를 배우는 것 만으로도 충분하다고 느낄수 있고 또 다른것을 배우고 써보는것은 지나치거나 불필요해 보일수 있습니다. 어쨋든 간에 우리의 설문조사 어플리케이션은 지금 꽤 잘 돌아고 있습니다. 문제들을 해쳐 나가며 자동화된 테스트를 만드는게 이 어플리케이션을 작동하게 하거나 더 좋게 하지는 않습니다. 만약 투표 어플리케이션을 만드는게 Django 프로그래밍의 최종 단계라면, 자동화된 테스트를 어떻게 만드는지 알 필요는 없습니다. 하지만 아직 더 많은것을 하려 한다면 지금이 자동화된 테스트 작성을 배우기 딱 좋은 시간입니다.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>테스트를 통해 시간을 절약 할 수 있습니다.<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>특정 시점까지는 ‘제대로 작동하는지 확인’ 하는것이 테스트로서 충분할 것 입니다. 더 정교한 어플리케이션에서는 구성 요소간에 수십 개의 복잡한 상호 작용이있을 수 있습니다.</p>\n<p>이들 컴포넌트들에 어떠한 변경이라도 생기게 되면 어플리케이션의 동작에 예상치 못했던 결과가 발생할수 있습니다. 이 상황에서 ‘제대로 작동하는지 확인’한다는 것은 20개의 서로 다른 테스트 데이터를 가지고 코드의 기능들을 돌려보는것입니다. 단지 당신이 뭔가를 망가뜨리지 않았다는것을 확인하기 위해서 말이죠 - 시간은 귀한데 말입니다.</p>\n<p>이 수동 테스트 작업을 자동화된 테스트가 몇초만에 해낼수 있다면 귀한시간을 많이 아낄수 있겠죠?. 무언가가 잘못되어도 테스트를 통해 예기치 않은 동작을 일으키는 코드를 식별하는 데 도움이됩니다.</p>\n<p>때로는 코드가 제대로 작동하고 있음을 알 때 테스트를 작성하는 것은 허드렛일로 보여서 당신의 생산적이고 창의적인 프로그래밍 작업에서 떠나 매력적이지도 흥분되지도 않는 테스트 작성이라는 일을 하는게 어려울수도 있습니다.</p>\n<p>그러나 테스트를 작성하는 작업은 어플리케이션을 수동으로 테스트하거나 새로 발견된 문제의 원인을 확인하는 데 많은 시간을 투자하는 것보다 훨씬 더 효과적입니다.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>테스트는 문제를 그저 식별하는 것이 아니라 예방합니다.<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>테스트를 그저 개발의 부정적 측면으로 생각하는 것은 실수입니다.</p>\n<p>테스트가 없으면 어플리케이션의 목적 또는 의도 된 동작이 다소 불투명 할 수 있습니다. 심지어 자신의 코드 일 때도, 정확히 무엇을하고 있는지 알아 내려고 노력하게 됩니다.</p>\n<p>테스트는 이 불투명함을 바꿉니다. 그들은 내부에서 코드를 밝혀 내고, 어떤 것이 잘못 될 때, <em>그것이 잘못되었다는 것을 깨닫지 못했다고 할지라도, 잘못된 부분에 빛을 집중시킵니다</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>테스트가 코드를 더 매력적으로 만듭니다.<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>당신이 화려한 소프트웨어를 만들더라도, 다른 많은 개발자들은 그저 테스트가 부족하기 때문에 그것을 사용하려 하지 않을 것입니다. 테스트 없이 그들은 그것을 신뢰하지 않을 것입니다. Django의 원 개발자 중 한 명인 Jacob Kaplan-Moss는 “테스트없는 코드는 설계상 망가져 있는것” 이라고 말합니다.</p>\n<p>테스트 작성을 시작해야하는 또다른 이유는 다른 개발자들이 당신의 소프트웨어를 사용하는것을 진지하게 고려하기 전에 테스트 코드를 보기를 원하기 때문입니다.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>테스트는 팀이 함께 일하는것을 돕습니다<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>이전의 내용은 어플리케이션을 유지 관리하는 단일 개발자의 관점에서 작성되었습니다. 복잡한 애플리케이션은 팀별로 유지 관리됩니다. 테스트는 동료가 실수로 코드를 손상시키지 않는다는 것을 보증합니다 (그리고 당신이 동료의 코드를 모르는새에 망가트리는것도). 장고 프로그래머로서 생계를 꾸려 나가려면 테스트를 잘해야합니다!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>기초 테스팅 전략<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>테스트 작성에 대한 많은 접근법이 있습니다.</p>\n<p>어떤 프로그래머들은 “<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Test-driven_development\">테스트 주도 개발</a>”이라는 원칙을 따릅니다. 그들은 정말로 코드를 작성하기도 전에 테스트를 먼저 작성합니다. 이는 직관에 위배되는 것처럼 보일 수 있지만, 사실은 대부분의 사람들이 자주 하는 방식과 비슷합니다. 문제를 서술한 뒤, 코드를 만들어서 그것을 풀어내는 것이죠. 테스트 주도 개발은 파이썬 테스트 케이스로 문제를 간추려 공식화합니다.</p>\n<p>더 흔하게는, 테스팅 입문자들은 코드를 작성하고 시간이 흐른 뒤에 테스트들이 필요하다고 판단할 것입니다. 아마도 몇몇의 테스트는 더 빨리 작성하는 것이 나을지도 모릅니다. 하지만 시작하기에 너무 늦어서는 안 됩니다.</p>\n<p>어디서부터 테스트를 작성해야 할지 종잡을 수 없을 때가 있습니다. 당신이 수천 줄의 파이썬 코드를 작성해 놓았다면, 테스트할 것을 고르는 것이 쉽지 않을지도 모릅니다. 그럴 때는 다음에 새로운 기능을 넣거나 버그를 수정하는 등, 코드를 변경할 일이 있을 때, 당신의 첫 테스트를 작성하는 것이 유익할 것입니다.</p>\n<p>그러니 지금 당장 해봅시다.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>첫 번째 테스트 작성하기<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>버그 식별하기<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>다행스럽게도 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 어플리케이션에는 우리가 즉시 해결할 수있는 약간의 버그가 있습니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> 메소드는 <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>이 어제 게시된 경우 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>를 반환(올바른 동작)할 뿐만 아니라 <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>의 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> 필드가 미래로 설정되어 있을 때도 그렇습니다(틀린 동작).</p>\n<p><a class=\"reference internal\" href=\"/ko/2.1/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-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=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">polls.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create a Question instance with pub_date 30 days in the future</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># was it published recently?</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">()</span>\n<span class=\"go\">True</span>\n</code></pre></div>\n<p>미래는 ‘최근(recent)’이 아니기 때문에 이는 분명히 잘못된 것입니다.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>버그를 노출하는 테스트 만들기<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>문제를 테스트하기 위해 <a class=\"reference internal\" href=\"/ko/2.1/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>애플리케이션 테스트는 일반적으로 애플리케이션의 <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> 파일에 있습니다. 테스트 시스템은 <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code> 로 시작하는 파일에서 테스트를 자동으로 찾습니다.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 어플리케이션의 <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> 파일에 다음을 입력하십시오:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</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\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>우리는 미래의 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>를 가진 Question` 인스턴스를 생성하는 메소드를 가진 <a class=\"reference internal\" href=\"/ko/2.1/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> 하위 클래스를 생성했습니다. 그런 다음 <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>의 출력이 False가 되는지 확인했습니다.</p>\n</section>\n<section id=\"running-tests\">\n<h3>테스트 실행<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>터미널에서 테스트를 실행합니다.</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>그러면 다음과 같은 것을 볼 수 있습니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"n\">F</span>\n<span class=\"o\">======================================================================</span>\n<span class=\"n\">FAIL</span><span class=\"p\">:</span> <span class=\"n\">test_was_published_recently_with_future_question</span> <span class=\"p\">(</span><span class=\"n\">polls</span><span class=\"o\">.</span><span class=\"n\">tests</span><span class=\"o\">.</span><span class=\"n\">QuestionModelTests</span><span class=\"p\">)</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Traceback</span> <span class=\"p\">(</span><span class=\"n\">most</span> <span class=\"n\">recent</span> <span class=\"n\">call</span> <span class=\"n\">last</span><span class=\"p\">):</span>\n  <span class=\"n\">File</span> <span class=\"s2\">&quot;/path/to/mysite/polls/tests.py&quot;</span><span class=\"p\">,</span> <span class=\"n\">line</span> <span class=\"mi\">16</span><span class=\"p\">,</span> <span class=\"ow\">in</span> <span class=\"n\">test_was_published_recently_with_future_question</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n<span class=\"ne\">AssertionError</span><span class=\"p\">:</span> <span class=\"kc\">True</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"kc\">False</span>\n\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">FAILED</span> <span class=\"p\">(</span><span class=\"n\">failures</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>무슨 일이 일어 났습니까?</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>는 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 애플리케이션에서 테스트를 찾습니다.</p></li>\n<li><p><a class=\"reference internal\" href=\"/ko/2.1/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>테스트 목적으로 특별한 데이터베이스를 만들었습니다.</p></li>\n<li><p>테스트 메소드 - 이름이 <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>로 시작하는 것들을 찾습니다.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code>에서 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>필드가 30일 미래인 <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> 인스턴스를 생성했습니다</p></li>\n<li><p>… <code class=\"docutils literal notranslate\"><span class=\"pre\">assertIs()</span></code> 메소드를 사용하여, 우리가 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code>가 반환되기를 원함에도 불구하고 <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> 가 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>를 반환한다는 것을 발견했습니다.</p></li>\n</ul>\n<p>테스트는 어떤 테스트가 실패했는지와 실패가 발생한 행까지 알려줍니다.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>버그 수정<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>우리는 이미 문제가 무엇인지 알고 있습니다: <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code>는 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>가 미래에 있다면 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code>를 반환해야 합니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>에서 날짜가 과거에 있을 때에만 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>를 반환하도록 메소드를 수정하십시오.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/models.py</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>이제 테스트를 다시 실행합니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"o\">.</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">OK</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>버그를 확인한 후에 우리는 이를 드러내는 테스트를 작성 하였으며 코드에서 버그를 수정하고 테스트를 통과했습니다.</p>\n<p>앞으로는 다른 많은 것들이 우리 어플리케이션에 잘못 될 수도 있지만 단순히 테스트를 실행하면 우리에게 즉시 경고하므로 실수로 이 버그가 재발하지는 않을 것입니다. 우리는 애플리케이션의이 작은 부분을 영원히 안전한 상태로 고정시켰다고 생각할 수 있습니다.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>보다 포괄적인 테스트<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>우리가 여기있는 동안, 우리는 <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>메소드를 고정하는것 이상을 할수 있습니다; 사실 하나의 버그를 고치면서 다른 새로운 버그를 만들어 낸다면 분명 곤란할것입니다.</p>\n<p>메소드의 동작을보다 포괄적으로 테스트하기 위해 동일한 클래스에 두 가지 테스트 메소드를 추가하십시오:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</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<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>이제 우리는 <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code>가 과거, 최근, 미래의 질문에 대해 올바른 값을 반환한다는걸 확인시켜주는 세가지 테스트를 가졌습니다.</p>\n<p>다시 말하지만 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문조사</span> <span class=\"pre\">(polls)</span></code>는 단순한 프로그램이지만 추후에 복잡도가 증가되고 다른 어떤 코드들과 상호작용을 하더라도 테스트를 작성한 메소드가 예상대로 동작할 것이라는 보장을 할 수 있습니다.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>뷰 테스트<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>설문조사 어플리케이션은 상당히 대충대충 만들어져 있습니다. 이 어플리케이션은 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>필드가 미래에있는 질문 까지도 포함하여 게시합니다. 이것을 개선 해야합니다. 미래로 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>를 설정하는 것은 그 시기가 되면 질문이 게시되지만 그때까지는 보이지 않는 것을 의미 해야 합니다.</p>\n<section id=\"a-test-for-a-view\">\n<h3>뷰에 대한 테스트<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>위의 버그를 수정했을 때 먼저 테스트를 작성한 다음 코드 수정을 했습니다. 사실 그것은 테스트 주도 개발의 간단한 예제 였지만 어떤 순서로 작업하는지는 중요하지 않습니다.</p>\n<p>첫 번째 테스트에서 코드의 내부 동작에 대해 자세히 설명했습니다. 이 테스트에서는 웹 브라우저를 통해 사용자가 경험하는대로 동작을 확인하려고합니다.</p>\n<p>버그를 수정하기 전에 우리가 사용 할 수있는 도구를 살펴 보겠습니다.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>장고 테스트 클라이언트<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는 뷰 레벨에서 코드와 상호 작용하는 사용자를 시뮬레이트하기위해 테스트 클라이언트 클래스 <a class=\"reference internal\" href=\"/ko/2.1/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>를 제공합니다. 이 테스트 클라이언트를 <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>또는 <a class=\"reference internal\" href=\"/ko/2.1/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>우리는 또다시 <a class=\"reference internal\" href=\"/ko/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>에서 시작할 것인데, <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>에서 필요하지 않았던 두 가지 일을 해야 합니다. 첫 번째는 <a class=\"reference internal\" href=\"/ko/2.1/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>response.context와 같은 response의 추가적인 속성을 사용할수 있게 하기위해서 <a class=\"reference internal\" href=\"/ko/2.1/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>를 사용하여 템플릿 렌더러를 설치합니다. 이 메소드는 테스트 데이터베이스를 셋업하지 <em>않습니다</em>. 그렇기 때문에 테스트는 현재 사용중인 데이터베이스 위에서 돌게되며 결과는 데이터베이스에 이미 만들어져있는 질문들에 따라서 조금씩 달라질 수 있습니다. 또한 <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code>의 <code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code>이 올바르지 않으면 예기치 않은 결과가 발생할 수 있습니다. 초기에 어떻게 설정해놨는지 기억나지 않는다면 진행하기 전에 먼저 확인하십시오.</p>\n<p>다음으로 우리는 테스트 클라이언트 클래스를 import 해야합니다. (나중에 <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>에서는 <a class=\"reference internal\" href=\"/ko/2.1/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>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Client</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create an instance of the client for our use</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">client</span> <span class=\"o\">=</span> <span class=\"n\">Client</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>이런것들이 준비가 되었다면 이제 우리는 클라이언트에세 우리를 위해 일을 하라고 시킬수 있습니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># get a response from &#39;/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&#39;/&#39;</span><span class=\"p\">)</span>\n<span class=\"go\">Not Found: /</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we should expect a 404 from that address; if you instead see an</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># &quot;Invalid HTTP_HOST header&quot; error and a 400 response, you probably</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># omitted the setup_test_environment() call described earlier.</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">404</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># on the other hand we should expect to find something at &#39;/polls/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we&#39;ll use &#39;reverse()&#39; rather than a hardcoded URL</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">200</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">content</span>\n<span class=\"go\">b&#39;\\n    &lt;ul&gt;\\n    \\n        &lt;li&gt;&lt;a href=&quot;/polls/1/&quot;&gt;What&amp;#39;s up?&lt;/a&gt;&lt;/li&gt;\\n    \\n    &lt;/ul&gt;\\n\\n&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">]</span>\n<span class=\"go\">&lt;QuerySet [&lt;Question: What&#39;s up?&gt;]&gt;</span>\n</code></pre></div>\n</section>\n<section id=\"improving-our-view\">\n<h3>뷰를 개선시키기<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>설문 조사 목록에는 아직 게시되지 않은 설문 조사 (즉, 장래에 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>가 있는 설문 조사)가 표시됩니다. 그것을 수정합시다.</p>\n<p><a class=\"reference internal\" href=\"/ko/2.1/intro/tutorial04/\"><span class=\"doc\">튜토리얼 4장</span></a>에서 <a class=\"reference internal\" href=\"/ko/2.1/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\">polls/views.py</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=\"s1\">&#39;polls/index.html&#39;</span>\n    <span class=\"n\">context_object_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;latest_question_list&#39;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;Return the last five published questions.&quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p>우리는 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> 메소드를 수정하여 <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>와 비교하여 날짜를 검사하도록 변경해야 합니다. 먼저 가져 오기를 추가해야 합니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</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>그리고 다음과 같이 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> 메소드를 수정해야합니다:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</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>\n        <span class=\"n\">pub_date__lte</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span>\n    <span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">Question.objects.filter</span> <span class=\"pre\">(pub_date__lte</span> <span class=\"pre\">=</span> <span class=\"pre\">timezone.now</span> <span class=\"pre\">())</span></code>는 <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now</span></code>보다 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>가 작거나 같은 <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>을 포함하는 queryset을 반환합니다.</p>\n</section>\n<section id=\"testing-our-new-view\">\n<h3>새로운 뷰 테스트<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>이제 <code class=\"docutils literal notranslate\"><span class=\"pre\">runserver</span></code>를 실행하면 브라우저에 사이트가 적재되고 과거와 미래 날짜의 <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>이 생성되고, 퍼블리시된 것들만 리스트에 나타나는 것을 확인으로써 예상대로 동작하는 것에 만족할 수 있을 것입니다. 앞으로 <em>어떤 변화를 일으키더라도 이러한 점에 영향을 끼치지 않도록</em> 하고 싶을 것이므로, 위의 <a class=\"reference internal\" href=\"/ko/2.1/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><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\">polls/tests.py</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>새로운 테스트 클래스와 함께 질문들을 생성하는 함수를 만들 것입니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</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=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">200</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the past are displayed on the</span>\n<span class=\"sd\">        index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the future aren&#39;t displayed on</span>\n<span class=\"sd\">        the index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question_and_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Even if both past and future questions exist, only past questions</span>\n<span class=\"sd\">        are displayed.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_two_past_questions</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The questions index page may display multiple questions.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 1.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 2.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question 2.&gt;&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;&lt;Question: Past question 1.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n</code></pre></figure>\n<p>이 중 일부를 더 자세히 살펴 보겠습니다.</p>\n<p>먼저, 질문 생성 함수인 <code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>은 테스트 과정 중 설문을 생성하는 부분에서 반복 사용합니다.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code>는 질문을 생성하지는 않지만 “사용가능한 투표가 없습니다.”라는 메시지 및 <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code>가 비어 있음을 확인합니다. <a class=\"reference internal\" href=\"/ko/2.1/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> 클래스는 몇 가지 추가적인 선언 메소드를 제공합니다. 이 예제에서 우리는 <a class=\"reference internal\" href=\"/ko/2.1/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>와 <a class=\"reference internal\" href=\"/ko/2.1/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><code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code>에서 우리는 질문을 생성하고 그 질문이 리스트에 나타나는지 확인합니다.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code>에서 우리는 미래의 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>로 질문을 만듭니다. 데이터베이스는 각 테스트 메소드마다 재설정되므로 첫 번째 질문은 더 이상 존재하지 않으므로 다시 인덱스에 질문이 없어야 합니다.</p>\n<p>요컨데, 사이트에서 관리자 입력 및 사용자 경험에 대한 이야기를 하는 테스트를 만들었고, 모든 상태와 시스템 상태의 모든 새로운 변경 사항에 대해 예상하는 결과가 출력되는지 확인합니다.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3><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>우리가 만든 것이 잘 작동합니다. 그러나 미래의 설문들은 <em>목록</em>에 나타나지는 않지만, 사용자가 URL을 알고 있거나, 추측하면 접근할 수 있습니다. 그래서 우리는 <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\">polls/views.py</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    <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>물론, 우리는 시간이 지난 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> 값을 가지고 있는 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문</span></code>은 표시되고, 미래의 <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>는 표시되지 않게 몇 가지 검사를 추가 할 것입니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</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=\"s1\">&#39;Future question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">404</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The detail view of a question with a pub_date in the past</span>\n<span class=\"sd\">        displays the question&#39;s text.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">past_question</span> <span class=\"o\">=</span> <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s1\">&#39;Past Question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">question_text</span><span class=\"p\">)</span>\n</code></pre></figure>\n</section>\n<section id=\"ideas-for-more-tests\">\n<h3>더 많은 테스트를위한 아이디어<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>우리는 비슷한 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> 메소드를 <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code>에 추가하고 그 뷰에 대한 새로운 테스트 클래스를 생성해야합니다. 그것은 우리가 방금 만든 것과 매우 유사합니다. 사실 계속 반복 작업을 할 것입니다.</p>\n<p>테스트를 추가하면서 다른 방법으로 애플리케이션을 개선 할 수도 있습니다. 예를 들어, <code class=\"docutils literal notranslate\"><span class=\"pre\">선택</span> <span class=\"pre\">사항</span></code>이 없는 사이트에 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문</span></code>을 게시 할 수 있다는 것은 바보같은 일입니다. 그래서 우리의 뷰는 이를 확인하고 그러한 질문을 배제 할 것입니다. 우리의 테스트는 <code class=\"docutils literal notranslate\"><span class=\"pre\">선택사항</span></code>이 없는 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문</span></code>을 생성 한 다음, 실제로 게시되지 않는지 테스트하고, <code class=\"docutils literal notranslate\"><span class=\"pre\">선택사항</span></code>이 있는 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문</span></code>을 작성하고 게시 여부를 테스트합니다.</p>\n<p>아마도 일반 사용자가 아닌 로그인 한 관리자는 게시되지 않은 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문</span></code>을 볼 수 있어야합니다. 다시 말하면: 소프트웨어를 추가하기 위해 필요한 것은 무엇이든 테스트를 수반해야합니다, 먼저 테스트를 작성한 다음 코드가 테스트를 통과하게 만들 것인지, 아니면 먼저 코드에서 로직을 처리 한 다음 이를 증명할 테스트를 작성하십시오.</p>\n<p>어느 순간엔가 너무 많은 테스트 코드들을 보고 관리하기 힘들도록 너무 비대해 지는것은 아닌가 생각할수도 있습니다.</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>테스트 할 때는, 많이 할 수록 좋습니다.<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>우리의 테스트가 통제 불능으로 성장하고있는 것처럼 보일 수 있습니다. 이 속도라면 곧 우리의 어플리케이션에서 보다 우리의 테스트의 코드가 더 많아질 것이고,  나머지 코드의 우아한 간결함과 비교했을 때, 반복하는 것은 미학적입니다.</p>\n<p><strong>사실 비대해지는것은 중요하지 않습니다</strong>. 테스트 코드들이 늘어나게 하십시오. 대부분의 경우 테스트를 한 번 작성한 다음 신경을 끄게 됩니다. 그래도 이 테스트 코드의 유용한 기능들은 프로그램을 개발하는 동안 계속 해서 작동할것입니다.</p>\n<p>때로는 테스트를 업데이트해야합니다. 우리가 <code class=\"docutils literal notranslate\"><span class=\"pre\">선택지</span></code>를 가진 <code class=\"docutils literal notranslate\"><span class=\"pre\">설문들</span></code>만 출력되도록 뷰를 수정한다고 가정 해 보겠습니다. 이 경우 기존 테스트 중 상당수가 실패 할 것입니다. <em>테스트 결과를 최신으로 유지하기 위해 어떤 테스트를 수정해야하는지 정확하게 알려주므로</em> 테스트가 스스로를 돌보는 데 도움이됩니다.</p>\n<p>최악의 경우 개발을 계속할 때 중복되는 테스트가 있을 수 있습니다. 그것은 문제가 아닙니다.  테스팅에서 반복하는 것은 <em>좋은</em> 일입니다.</p>\n<p>테스트들이 현명하게 배열되어있는 한 관리가 어려워지지 않을 것입니다. 경험에 근거한 좋은 방법 중에는 다음과 같은 내용이 있습니다.</p>\n<ul class=\"simple\">\n<li><p>각 모델이나 뷰에 대한 별도의 <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code></p></li>\n<li><p>테스트하려는 각 조건 집합에 대해 분리된 테스트 방법</p></li>\n<li><p>기능를 설명하는 테스트 메소드 이름</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>추가 테스팅<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>이 튜토리얼에서는 테스트의 기본 사항에 대해서만 소개합니다. 여러분은 더 많은 것을 할 수도 있고,  또 사용할 수 있는 똑똑한 도구들이 많이 있습니다.</p>\n<p>예를 들어, 이 전에 수행 한 테스트에서는 모델의 내부 로직과 뷰에서 정보를 게시하는 방법을 다루었지만 <a class=\"reference external\" href=\"http://seleniumhq.org/\">Selenium</a> 같은 “브라우저 내” 프레임 워크를 사용하여 HTML이 브라우저에서 실제로 렌더링되는 방식을 테스트 할 수 있습니다. 이러한 도구를 사용하면 장고 코드의 동작뿐만 아니라 JavaScript도 확인할 수 있습니다. 테스트가 브라우저를 시작하고 인간이 그것을 다루는 것처럼 사이트와 상호 작용 것은 매우 중요합니다! Django에는 <a class=\"reference internal\" href=\"/ko/2.1/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>가 포함되어있어 Selenium과 같은 도구와 쉽게 통합할 수 있게 해줍니다.</p>\n<p>복잡한 어플리케이션을 사용하는 경우 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Continuous_integration\">연속적으로 통합</a>하기 위해 모든 커밋마다 자동으로 테스트를 실행하여 품질 제어가 적어도 부분적으로 자동화되도록 할 수 있습니다.</p>\n<p>어플리케이션에서 테스트되지 않은 부분을 탐지하는 좋은 방법은 코드 커버리지를 확인하는 것입니다. 이것은 또한 깨지기 쉬운 코드나 심지어는 죽은 코드를 식별하는 데 도움이됩니다. 코드를 테스트 할 수 없다는 것은 대개 코드가 리팩터링해야하거나 제거해야 함을 의미합니다. 커버리지는 죽은 코드를 확인하는 데 도움이됩니다. 자세한 내용은 <a class=\"reference internal\" href=\"/ko/2.1/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Integration with coverage.py</span></a> 를 참조하십시오.</p>\n<p><a class=\"reference internal\" href=\"/ko/2.1/topics/testing/\"><span class=\"doc\">장고 테스트</span></a>는 테스트에 대한 포괄적인 정보를 제공합니다.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>다음 내용은?<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>테스트에 대한 자세한 내용은 <a class=\"reference internal\" href=\"/ko/2.1/topics/testing/\"><span class=\"doc\">장고 테스트</span></a>를 참조하십시오.</p>\n<p>Django 뷰를 테스트하는 것이 익숙해졌으면, 정적 파일 관리에 대해 배울 수 있는 <a class=\"reference internal\" href=\"/ko/2.1/intro/tutorial06/\"><span class=\"doc\">이 튜토리얼의 6장</span></a>을 보세요.</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"자동화된 테스트 소개","anchor":"introducing-automated-testing","children":[{"title":"자동화된 테스트란 무엇입니까?","anchor":"what-are-automated-tests","children":[]},{"title":"테스트를 만들어야하는 이유","anchor":"why-you-need-to-create-tests","children":[{"title":"테스트를 통해 시간을 절약 할 수 있습니다.","anchor":"tests-will-save-you-time","children":[]},{"title":"테스트는 문제를 그저 식별하는 것이 아니라 예방합니다.","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"테스트가 코드를 더 매력적으로 만듭니다.","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"테스트는 팀이 함께 일하는것을 돕습니다","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"기초 테스팅 전략","anchor":"basic-testing-strategies","children":[]},{"title":"첫 번째 테스트 작성하기","anchor":"writing-our-first-test","children":[{"title":"버그 식별하기","anchor":"we-identify-a-bug","children":[]},{"title":"버그를 노출하는 테스트 만들기","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"테스트 실행","anchor":"running-tests","children":[]},{"title":"버그 수정","anchor":"fixing-the-bug","children":[]},{"title":"보다 포괄적인 테스트","anchor":"more-comprehensive-tests","children":[]}]},{"title":"뷰 테스트","anchor":"test-a-view","children":[{"title":"뷰에 대한 테스트","anchor":"a-test-for-a-view","children":[]},{"title":"장고 테스트 클라이언트","anchor":"the-django-test-client","children":[]},{"title":"뷰를 개선시키기","anchor":"improving-our-view","children":[]},{"title":"새로운 뷰 테스트","anchor":"testing-our-new-view","children":[]},{"title":"DetailView 테스트하기","anchor":"testing-the-detailview","children":[]},{"title":"더 많은 테스트를위한 아이디어","anchor":"ideas-for-more-tests","children":[]}]},{"title":"테스트 할 때는, 많이 할 수록 좋습니다.","anchor":"when-testing-more-is-better","children":[]},{"title":"추가 테스팅","anchor":"further-testing","children":[]},{"title":"다음 내용은?","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"시작하기","url":"/ko/2.1/intro/"}],"prev":{"docname":"intro/tutorial04","title":"첫 번째 장고 앱 작성하기, part 4","url":"/ko/2.1/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"첫 번째 장고 앱 작성하기, part 6","url":"/ko/2.1/intro/tutorial06/"},"formats":{"html":"/ko/2.1/intro/tutorial05/","markdown":"/ko/2.1/intro/tutorial05.md","json":"/ko/2.1/intro/tutorial05.json"},"source":"https://github.com/django/django/blob/stable/2.1.x/docs/intro/tutorial05.txt","official":"https://docs.djangoproject.com/ko/2.1/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"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}