{"title":"Escribiendo su primera aplicación en Django, parte 5","version":"2.1","locale":"es","docname":"intro/tutorial05","url":"/es/2.1/intro/tutorial05/","canonical":"https://djangodocs.dev/es/2.1/intro/tutorial05/","summary":"Este tutorial comienza donde quedó el Tutorial 4 . Hemos creado una aplicación Web de encuestas y ahora vamos a crear algunas pruebas automatizadas para ella.…","html":"<h1>Escribiendo su primera aplicación en Django, parte 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>Este tutorial comienza donde quedó el <a class=\"reference internal\" href=\"/es/2.1/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a>. Hemos creado una aplicación Web de encuestas y ahora vamos a crear algunas pruebas automatizadas para ella.</p>\n<section id=\"introducing-automated-testing\">\n<h2>Presentando las pruebas automatizadas<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>¿Qué son las pruebas automatizadas?<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>Las pruebas son simples rutinas que comprueban el funcionamiento de su código.</p>\n<p>Las pruebas se ejecutan en diferentes niveles. Algunas pruebas se pueden aplicar a un pequeño detalle (<em>¿Un método de modelo particular retorna valores como se espera?</em>) mientras que otras examinan el total funcionamiento del software (<em>¿Una secuencia de entradas de usuarios en el sitio produce el resultado deseado?</em>). No se diferencia del tipo de pruebas que ejecutó anteriormente en el <a class=\"reference internal\" href=\"/es/2.1/intro/tutorial02/\"><span class=\"doc\">Tutorial 2</span></a> utilizando el comando <a class=\"reference internal\" href=\"/es/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> para examinar el comportamiento de un método, o ejecutar la aplicación e ingresar datos para comprobar cómo se comporta.</p>\n<p>Lo que es diferente en las pruebas <em>automatizadas</em> es que el trabajo de pruebas por usted lo hace el sistema. Usted crea un conjunto de pruebas una vez, después, a medida que modifica su aplicación, puede comprobar que el código todavía funciona como originalmente lo concibió, sin necesidad de pruebas manuales engorrosas.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>El por qué necesita crear pruebas<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>Entonces, ¿por qué crear pruebas y por qué ahora?</p>\n<p>Usted podría pensar que ya con aprender Python y Django tiene bastante con qué ocuparse, y que tener que aprender y hacer aún más cosas podría ser abrumador y hasta innecesario. Después de todo, nuestra aplicación encuestas está funcionando ahora bastante bien, tomarse la molestia de crear pruebas automatizadas no va a hacer que funcione mejor. Si crear la aplicación encuestas es el último fragmento de programa de Django que hará, entonces es verdad, usted no necesita saber cómo crear pruebas automatizadas. Sin embargo, si ese no es el caso, ahora es un momento excelente para aprender.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>Las pruebas le ahorrarán tiempo<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>Hasta cierto punto, “comprobar de que parece funcionar” será una prueba satisfactoria. En una aplicación más sofisticada, es posible que tenga decenas de interacciones complejas entre los componentes.</p>\n<p>Una cambio en cualquiera de esos componentes podría tener consecuencias inesperadas en el comportamiento de la aplicación. Comprobar que todavía “parece funcionar” podría significar pasar por la funcionalidad de su código con veinte variaciones diferentes de sus datos de prueba solo para asegurarse de que no ha roto algo; lo cual no es un buen uso de su tiempo.</p>\n<p>Eso es especialmente cierto cuando las pruebas automatizadas pueden hacer esto por usted en cuestión de segundos. Si algo salió mal, las pruebas también ayudarán a identificar el código que está causando el comportamiento inesperado.</p>\n<p>A veces puede parecer un fastidio apartarse de su trabajo creativo y productivo de programación para afrontar la obligación poca atractiva y aburrida de escribir pruebas, particularmente cuando sabe que su código funciona de forma adecuada.</p>\n<p>Sin embargo, la tarea de escribir pruebas es mucho más gratificante que pasar horas probando su aplicación de forma manual o intentando identificar la causa de un problema recién introducido.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>Las pruebas no solo identifican los problemas, los previenen<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>Es un error pensar en las pruebas sólo como un aspecto negativo del desarrollo.</p>\n<p>Sin las pruebas, el propósito o el comportamiento previsto de una aplicación podría ser bastante incomprensible. Aún cuando es su propio código, usted a veces se encontrará buscando en él tratando de averiguar qué es exactamente lo que hace.</p>\n<p>Las pruebas cambian eso, ellas iluminan su código desde adentro, y cuando algo sale mal, ellas se centran en la parte que salió mal; <em>incluso si usted no se ha dado cuenta de que salió mal</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>Las pruebas hacen más atractivo su código<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>Usted puede haber creado una brillante pieza de software, pero encontrará que muchos otros desarrolladores simplemente se negarán a verla porque carece de pruebas; sin pruebas, no van a confiar en ella. Jacob Kaplan-Moss, uno de los desarrolladores originales de Django, dice «El código sin pruebas es defectuoso por diseño.»</p>\n<p>Que otros desarrolladores quieran ver pruebas en su software antes de que se lo tomen en serio es una razón más para que usted comience a escribir las pruebas.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>Las pruebas ayudan a los equipos a trabajar en conjunto<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>Los puntos anteriores están escritos desde el punto de vista de un único desarrollador que mantiene una aplicación. Las aplicaciones complejas serán mantenidas por equipos. Las pruebas garantizan que los colegas no rompan inadvertidamente su código (y que usted no rompa el de ellos sin saber). Si quiere ganarse la vida como un programador de Django, ¡Usted debe ser bueno escribiendo pruebas!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>Estrategias básicas de pruebas<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>Hay muchas maneras de abordar la escritura de pruebas.</p>\n<p>Algunos programadores siguen una disciplina llamada «<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Test-driven_development\">desarrollo basado en pruebas</a>»; ellos efectivamente escriben las pruebas antes de escribir el código. Esto puede parecer ir contra el sentido común, pero en realidad es similar a lo que la mayoría de la gente a menudo hará de todos modos: describen un problema, a continuación, crean un código para resolverlo. El desarrollo basado en pruebas simplemente formaliza el problema en un caso de prueba Python.</p>\n<p>La mayoría de las veces, un principiante en pruebas va a crear código y luego decidirá que este debe tener algunas pruebas. Tal vez hubiera sido mejor escribir algunas pruebas antes, pero nunca es demasiado tarde para empezar.</p>\n<p>A veces es difícil saber por dónde empezar con la escritura de pruebas. Si usted ha escrito varias miles de líneas de Python, elegir algo para probar podría no ser fácil. En tal caso, es provechoso escribir su primera prueba la próxima vez que realice una modificación, ya sea cuando agregue una nueva funcionalidad o solucione un error.</p>\n<p>Así que vamos a hacer eso de inmediato.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>Escribiendo nuestra primera prueba<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>Identificamos un bug<a class=\"heading-anchor\" href=\"#we-identify-a-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Afortunadamente, hay un pequeño bug en la aplicación <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> para que lo solucionemos de inmediato: el método <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> retorna <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> si la <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> fue publicada en el último día (que es correcto), pero también si el campo de <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> de <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> está en el futuro (que de hecho no lo está).</p>\n<p>Confirm the bug by using the <a class=\"reference internal\" href=\"/es/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> to check the method on a question\nwhose date lies in the future:</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>Puesto que las cosas en el futuro no son “recientes”, esto es claramente erróneo.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>Cree una prueba para mostrar el bug<a class=\"heading-anchor\" href=\"#create-a-test-to-expose-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Lo que acabamos de hacer en el comando <a class=\"reference internal\" href=\"/es/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> para detectar el problema es exactamente lo que podemos hacer en una prueba automatizada, así que vamos a convertir eso en una prueba automatizada.</p>\n<p>Un lugar convencional para las pruebas de una aplicación se encuentra en el archivo <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> de la aplicación; el sistema de pruebas encontrará automáticamente las pruebas en cualquier archivo cuyo nombre comience con <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>.</p>\n<p>Ponga lo siguiente en el archivo <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> de la aplicación <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\">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>Here we have created a <a class=\"reference internal\" href=\"/es/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> subclass with a method that\ncreates a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> instance with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the future. We then check\nthe output of <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> - which <em>ought</em> to be False.</p>\n</section>\n<section id=\"running-tests\">\n<h3>Ejecutando las pruebas<a class=\"heading-anchor\" href=\"#running-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>In the terminal, we can run our test:</p>\n<div class=\"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>y usted verá algo como:</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>Esto es lo que ocurrió:</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> looked for tests in the <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> application</p></li>\n<li><p>encontró una subclase de la: clase: clase django.test.TestCase</p></li>\n<li><p>creó una base de datos especial para las pruebas</p></li>\n<li><p>buscó métodos de prueba; aquellos cuyos nombres comienzan con <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code></p></li>\n<li><p>en <code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code> creó una instancia <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> cuyo campo de <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> está 30 días en el futuro</p></li>\n<li><p>… y utilizando el método <code class=\"docutils literal notranslate\"><span class=\"pre\">assertIs()</span></code> descubrió que <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> retorna <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>, aunque queríamos que retornara <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code></p></li>\n</ul>\n<p>La prueba nos informa que prueba falló e incluso la línea en la que se produjo el error.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>Solucionando el bug<a class=\"heading-anchor\" href=\"#fixing-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Ya sabemos cuál es el problema: <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> debe devolver <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> si su <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> está en el futuro. Modifique el método <code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>, de modo que este sólo retornará <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> si la fecha está también en el pasado:</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>y ejecute la prueba de nuevo:</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>Después de identificar un bug, escribimos una prueba que lo expone y corregimos el bug en el código para que nuestra prueba pase.</p>\n<p>Muchas otras cosas podrían salir mal con nuestra aplicación en el futuro, pero podemos estar seguros de que no vamos a reintroducir este bug por descuido porque simplemente ejecutar la prueba nos avisará inmediatamente. Podemos considerar que esta pequeña parte de la aplicación está asegurada para siempre.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>Pruebas más exhaustivas<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>Ya que estamos aquí, podemos además precisar el método <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code>; de hecho, sería verdaderamente vergonzoso si al corregir un bug hubiéramos introducido otro.</p>\n<p>Agregue dos métodos de pruebas más a la misma clase, para probar el comportamiento del método de forma más completa:</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>Y ahora tenemos tres pruebas que confirman que <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> retorna valores razonables para preguntas pasadas, recientes y futuras.</p>\n<p>Una vez más, <code class=\"docutils literal notranslate\"><span class=\"pre\">encuestas</span></code> es una aplicación sencilla, pero por muy compleja que se vuelva en el futuro e independientemente de cualquier otro código con que interactúe, ahora tenemos alguna garantía de que el método para el cual hemos escrito las pruebas se comportará de la manera esperada.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>Pruebe una vista<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>La aplicación encuestas no discrimina en lo absoluto: publicará cualquier pregunta, incluyendo aquellas cuyo campo de <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> esté en el futuro. Deberíamos actualizar esto. Establecer un <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> en el futuro debería significar que la pregunta se publica en ese momento, pero que será invisible hasta entonces.</p>\n<section id=\"a-test-for-a-view\">\n<h3>Una prueba para una vista<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>Cuando corregimos el bug anterior, escribimos primero la prueba y luego el código para corregirlo. De hecho ese fue un simple ejemplo del desarrollo basado en pruebas, pero en realidad no importa en qué orden hacemos el trabajo.</p>\n<p>En nuestra primera prueba, nos centramos detalladamente en el comportamiento interno del código. Para esta prueba, queremos comprobar su comportamiento como lo experimentaría un usuario a través de un navegador web.</p>\n<p>Antes de tratar de corregir algo, echemos un vistazo a las herramientas a nuestra disposición.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>El cliente de prueba de 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 proporciona una prueba <a class=\"reference internal\" href=\"/es/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> para simular un usuario interactúando con el código al nivel de la vista. Podemos utilizarlo en <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> o incluso en el <a class=\"reference internal\" href=\"/es/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>We will start again with the <a class=\"reference internal\" href=\"/es/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>, where we need to do a couple of\nthings that won’t be necessary in <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. The first is to set up the test\nenvironment in the <a class=\"reference internal\" href=\"/es/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><a class=\"reference internal\" href=\"/es/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> instala un procesador de plantillas que nos permite examinar algunos atributos adicionales sobre respuestas como <code class=\"docutils literal notranslate\"><span class=\"pre\">response.context</span></code> que de otra forma no estarían disponibles. Tenga en cuenta que este método <em>no</em> crea una base de datos de pruebas, por lo que lo siguiente se ejecutará contra la base de datos existente y la salida puede variar levemente dependiendo de cuáles preguntas usted ya creó. Puede obtener resultados inesperados si su <code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code> en el archivo <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code> no es correcto. Si no recuerda haberlo configurado anteriormente, verifíquelo antes de continuar.</p>\n<p>A continuación tenemos que importar la clase de cliente de prueba (más tarde en <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> usaremos la clase <a class=\"reference internal\" href=\"/es/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> que viene con su propio cliente, por lo que esto no será necesario):</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>Con eso listo, podemos pedirle al cliente que haga un trabajo para nosotros:</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>Mejorando nuestra vista<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>La lista de encuestas muestra las encuestas que aún no están publicadas (es decir, aquellas que tienen una <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> en el futuro). Vamos a solucionar eso.</p>\n<p>En el <a class=\"reference internal\" href=\"/es/2.1/intro/tutorial04/\"><span class=\"doc\">Tutorial 4</span></a> presentamos una vista basada en clases, basada en <a class=\"reference internal\" href=\"/es/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>Tenemos que modificar el método <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> y cambiarlo para que también compruebe la fecha comparándolo con <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>. En primer lugar tenemos que añadir un import:</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>y luego hay que modificar el método <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> como sigue:</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(pub_date__lte=timezone.now())</span></code> retorna un queryset que contiene <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>s cuya <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> es menor o igual a, es decir, anterior o igual a <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now</span></code>.</p>\n</section>\n<section id=\"testing-our-new-view\">\n<h3>Probando nuestra nueva vista<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 <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> with\ndates in the past and future, and checking that only those that have been\npublished are listed. You don’t want to have to do that <em>every single time you\nmake any change that might affect this</em> - so let’s also create a test, based on\nour <a class=\"reference internal\" href=\"/es/2.1/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>Agregue lo siguiente a <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>y vamos a crear una función de atajo para crear preguntas, así como una nueva clase de pruebas:</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>Veamos algunas de estas más de cerca.</p>\n<p>En primer lugar es una función de atajo a las preguntas, <code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>, para eliminar parte de la repetición del proceso de creación de preguntas.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code> no crea ninguna pregunta, pero comprueba el mensaje: «No hay encuestas disponibles.» y verifica que <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> esté vacía. Tenga en cuenta que la clase <a class=\"reference internal\" href=\"/es/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> ofrece algunos métodos de aserción adicionales. En estos ejemplos utilizamos :meth:~django.test.SimpleTestCase.assertContains()` y <a class=\"reference internal\" href=\"/es/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>En <code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code> creamos una pregunta y verificamos que aparezca en la lista.</p>\n<p>En <code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code> creamos una pregunta con una <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> en el futuro. La base de datos se reinicia para cada método de prueba, por lo que la primera pregunta ya no está allí, de modo que de nuevo el índice no debería tener ninguna pregunta.</p>\n<p>Y así sucesivamente. En efecto, estamos utilizando las pruebas para contar una historia del ingreso de datos en el sitio administrativo y experiencia de usuario en el sitio, y comprobar que los resultados esperados se publican en cada estado y para cada nuevo cambio en el estado del sistema.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3>Probando la <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>Lo que tenemos funciona bien; sin embargo, a pesar de que las preguntas futuras no aparecen en el <em>índice</em>, los usuarios pueden todavía llegar a ellas si saben o adivinan la dirección URL correcta. Así que tenemos que agregar una restricción similar a la <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>Y por supuesto, vamos a agregar algunas pruebas para comprobar que una <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> cuya <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> que está en el pasado se puede mostrar y que una con una <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> en el futuro no se pueda:</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>Ideas para más pruebas<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>Debemos agregar un método <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> similar a <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code> y crear una nueva clase de pruebas para esa vista. Va a ser muy similar a lo que acabamos de crear; de hecho, habrá mucha repetición.</p>\n<p>También podríamos mejorar nuestra aplicación de otras maneras, agregando pruebas a lo largo del camino. Por ejemplo, es una tontería que se puedan publicar <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> en el sitio que no tienen <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code>. Por lo tanto, nuestras vistas podrían comprobar esto y excluir dichas <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>. Nuestras pruebas crearían una <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> sin <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> y luego probarían que no está publicada, así como crearían una <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> similar <em>con</em> <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> y probarían que <em>está</em> publicada.</p>\n<p>Quizás se les debería permitir a los usuarios registrados en el sitio administrativo ver las <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> que no han sido publicadas, pero no a los visitantes ordinarios. Una vez más: lo que se tenga que agregar al software para lograr esto debería ir acompañado de una prueba, ya sea que escriba la prueba primero y luego haga que el código pase la prueba, o primero solucione la lógica en el código y luego escriba una prueba para probarlo.</p>\n<p>En un momento dado usted está obligado a examinar sus pruebas y a preguntarse si su código sufre de exceso de pruebas, lo que nos lleva a:</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>Cuando se trata de pruebas, más es mejor<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>Podría parecer que nuestras pruebas están creciendo fuera de control. A este paso pronto habrá más código en nuestras pruebas que en nuestra aplicación y la repetición es poco estética en comparación con la refinada concisión del resto de nuestro código.</p>\n<p><strong>No importa</strong>, deje que crezcan. Por lo general, usted puede escribir una prueba una vez y luego olvidarse de ella. Esta seguirá cumpliendo su función útil mientras usted continúe desarrollando su programa.</p>\n<p>A veces las pruebas tendrán que ser actualizadas. Supongamos que modificamos nuestras vistas de modo que sólo las <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> con <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> se publiquen. En ese caso, muchas de nuestras pruebas existentes fallarán, <em>indicándonos exactamente qué pruebas necesitan ser modificadas para actualizarlas</em>, de modo que hasta ese punto las pruebas ayudan a ocuparse de sí mismas.</p>\n<p>En el peor de los casos, a medida que continúe desarrollando, usted podría encontrar que tiene algunas pruebas que ahora son redundantes. Incluso eso no es un problema, cuando se trata de pruebas, la redundancia es algo <em>bueno</em>.</p>\n<p>Siempre y cuando las pruebas se organicen de forma razonable, no van a ser difíciles de manejar. Las reglas básicas de uso incluyen el hecho de tener:</p>\n<ul class=\"simple\">\n<li><p>una <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code> independiente para cada modelo o vista</p></li>\n<li><p>un método de prueba independiente para cada conjunto de condiciones que usted quiere probar</p></li>\n<li><p>nombres de los métodos de prueba que describan su función</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>Pruebas adicionales<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>Este tutorial sólo presenta algunos de los temas fundamentales de las pruebas. Hay mucho más que usted puede hacer y una serie de herramientas muy útiles a su disposición para lograr algunas cosas muy interesantes.</p>\n<p>Por ejemplo, aunque nuestras pruebas han tratado aquí algo de la lógica interna de un modelo y la forma en que nuestras vistas publican información, usted puede utilizar un framework «en el navegador» como <a class=\"reference external\" href=\"http://seleniumhq.org/\">Selenium</a> para probar la forma en que su HTML en realidad se renderiza en un navegador. Estas herramientas le permiten comprobar no sólo el comportamiento de su código Django, sino también, por ejemplo, el de su JavaScript. ¡Es impresionante ver cómo las pruebas ejecutan un navegador y comienzan a interactuar con su sitio como si estuviese siendo operado por un humano! Django incluye <a class=\"reference internal\" href=\"/es/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> para facilitar la integración con herramientas como Selenium.</p>\n<p>Si usted tiene una aplicación compleja, es posible que desee ejecutar las pruebas de forma automática con cada commit a efectos de la integración continua_, por lo que el control de calidad está en sí, al menos parcialmente, automatizado.</p>\n<p>Una buena manera de detectar las partes de su aplicación que no han sido probadas es comprobar la cobertura de código. Esto también ayuda a identificar el código frágil o incluso muerto. Si no puede probar un trozo de código, por lo general significa que el código debe ser reestructurado o eliminado. La cobertura ayudará a identificar el código muerto. Consulte <a class=\"reference internal\" href=\"/es/2.1/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Integration with coverage.py</span></a> para más detalles.</p>\n<p><a class=\"reference internal\" href=\"/es/2.1/topics/testing/\"><span class=\"doc\">Las pruebas en Django</span></a> tiene información completa acerca de las pruebas.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>¿Qué sigue?<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>Para más detalles sobre las pruebas, consulte <a class=\"reference internal\" href=\"/es/2.1/topics/testing/\"><span class=\"doc\">Las pruebas en Django</span></a>.</p>\n<p>Cuando esté familiarizado con las pruebas de las vistas de Django, lea la <a class=\"reference internal\" href=\"/es/2.1/intro/tutorial06/\"><span class=\"doc\">parte 6 de este tutorial</span></a> para aprender sobre la gestión de los archivos estáticos.</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"Presentando las pruebas automatizadas","anchor":"introducing-automated-testing","children":[{"title":"¿Qué son las pruebas automatizadas?","anchor":"what-are-automated-tests","children":[]},{"title":"El por qué necesita crear pruebas","anchor":"why-you-need-to-create-tests","children":[{"title":"Las pruebas le ahorrarán tiempo","anchor":"tests-will-save-you-time","children":[]},{"title":"Las pruebas no solo identifican los problemas, los previenen","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"Las pruebas hacen más atractivo su código","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"Las pruebas ayudan a los equipos a trabajar en conjunto","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"Estrategias básicas de pruebas","anchor":"basic-testing-strategies","children":[]},{"title":"Escribiendo nuestra primera prueba","anchor":"writing-our-first-test","children":[{"title":"Identificamos un bug","anchor":"we-identify-a-bug","children":[]},{"title":"Cree una prueba para mostrar el bug","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"Ejecutando las pruebas","anchor":"running-tests","children":[]},{"title":"Solucionando el bug","anchor":"fixing-the-bug","children":[]},{"title":"Pruebas más exhaustivas","anchor":"more-comprehensive-tests","children":[]}]},{"title":"Pruebe una vista","anchor":"test-a-view","children":[{"title":"Una prueba para una vista","anchor":"a-test-for-a-view","children":[]},{"title":"El cliente de prueba de Django","anchor":"the-django-test-client","children":[]},{"title":"Mejorando nuestra vista","anchor":"improving-our-view","children":[]},{"title":"Probando nuestra nueva vista","anchor":"testing-our-new-view","children":[]},{"title":"Probando la DetailView","anchor":"testing-the-detailview","children":[]},{"title":"Ideas para más pruebas","anchor":"ideas-for-more-tests","children":[]}]},{"title":"Cuando se trata de pruebas, más es mejor","anchor":"when-testing-more-is-better","children":[]},{"title":"Pruebas adicionales","anchor":"further-testing","children":[]},{"title":"¿Qué sigue?","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"Empezando","url":"/es/2.1/intro/"}],"prev":{"docname":"intro/tutorial04","title":"Escribiendo su primera aplicación en Django, parte 4","url":"/es/2.1/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"Escribiendo su primera aplicación en Django, parte 6","url":"/es/2.1/intro/tutorial06/"},"formats":{"html":"/es/2.1/intro/tutorial05/","markdown":"/es/2.1/intro/tutorial05.md","json":"/es/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/es/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","1.10","1.9"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}