aboutsummaryrefslogtreecommitdiff
path: root/files/pt-br/learn/server-side/django/testing/index.html
blob: 19867b2ecae7be23cce9f6a28f307eb389654402 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
---
title: 'Tutorial Django Parte 10: Testando uma aplicação web Django'
slug: Learn/Server-side/Django/Testing
translation_of: Learn/Server-side/Django/Testing
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</div>

<p class="summary">À medida que websites crescem, eles se tornam mais difíceis de testar manualmente. Não apenas mais para testar, mas, as interações entre componentes tornam-se mais complexas, uma pequena mudança em uma área pode impactar outras áreas, portanto mais mudanças serão necessárias para garantir que tudo permaneça funcionando e erros não sejam introduzidos à medida que mais alterações forem feitas. Uma maneira de mitigar esses problemas é escrever testes automatizados, que podem ser executados facilmente e confiavelmente toda vez que você faz uma alteração. Este tutorial mostra como automatizar testes unitários do seu website utilizando o <em>framework</em> de testes do Django.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">Pré-requisitos:</th>
   <td>Complete todos os tópicos de tutoriais anteriores, incluindo <a href="/en-US/docs/Learn/Server-side/Django/Forms">Tutorial Django Parte 9: Trabalhando com formulários</a>.</td>
  </tr>
  <tr>
   <th scope="row">Objetivo:</th>
   <td>Entender como escrever testes unitários para websites baseados em Django.</td>
  </tr>
 </tbody>
</table>

<h2 id="Visão_Geral">Visão Geral</h2>

<p><a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Local Library</a> atualmente tem páginas para mostrar listas de todos livros e autores, visualização detalhada para itens <code>Book</code> e <code>Author</code>, uma página para renovar <code>BookInstance</code>s, e páginas para criar, atualizar e excluir itens <code>Author</code> (e também registros de <code>Book</code>, se você completou o desafio no <a href="/en-US/docs/Learn/Server-side/Django/Forms">forms tutorial</a>). Mesmo com este site relativamente pequeno, navegar manualmente por cada página e verificar superficialmente se tudo funciona como esperado pode levar vários minutos. À medida que fizemos mudanças e aumentamos o site, o tempo necessário para verificar manualmente se tudo funciona  "devidamente" só aumentará. Se continuássemos como estamos, eventuamente estaríamos gastando a maior parte do tempo testando, e muito pouco tempo aprimorando nosso código.</p>

<p>Testes automatizados podem realmente ajudar com este problema! Os benefícios óbvios são que eles podem ser executados muito mais rápido que testes manuais, podem testar com um nível mais baixo de detalhes, e testa exatamente a mesma funcionalidade (testadores humanos não são nem de longe tão confiáveis!). Por serem rápidos, testes automatizados podem ser executados mais regularmente, e se um teste falhar, eles apontam exatamente para onde o código não está funcionando como esperado .</p>

<p>Além disso, testes automatizados podem atuar como o primeiro "usuário" do mundo real do seu código, forçando você a ser rigoroso ao definir e documentar como seu website deve se comportar. Geralmente, eles são a base para seus exemplos de código e documentação. Por essas razões, alguns processos de desenvolvimento de código iniciam com definição e implementação de teste, o qual após o código é escrito para corresponder ao comportamento necessário (ex. <a href="https://en.wikipedia.org/wiki/Test-driven_development">desenvolvimento guiado por testes</a> e <a href="https://en.wikipedia.org/wiki/Behavior-driven_development">desenvolvimento guiado por comportamento</a>).</p>

<p>Este tutorial mostra como escrever testes automatizados para Django, adicionando um número de testes para o website <em>LocalLibrary</em>.</p>

<h3 id="Tipos_de_teste">Tipos de teste</h3>

<p>Há inúmeros tipos, níveis, e classificações de testes e abordagens de testes. Os testes automatizados mais importantes são:</p>

<dl>
 <dt>Testes unitários</dt>
 <dd>Verifica o comportamento funcional de componentes individuais, geralmente ao nível de classe e função.</dd>
 <dt>Testes de regressão</dt>
 <dd>Testes que reproduzem erros históricos. Cada teste é executado inicialmente para verificar se o erro foi corrigido, e então executado novamente para garantir que não foi reintroduzido após alterações posteriores no código.</dd>
 <dt>Testes de integração</dt>
 <dd>Verifica como agrupamentos de componentes funcionam quando utilizados  juntos. Testes de integração estão cientes das interações necessárias entre componentes, mas não necessariamente das operações internas de cada componente. Eles podem abranger agrupamentos simples de componentes através de todo website.</dd>
</dl>

<div class="note">
<p><strong>Nota: </strong>Outros tipos de testes comuns incluem caixa preta (black box), caixa branca (white box), manual, automatizado, canário (canary), fumaça (smoke), conformidade (conformance), aceitação (acceptance), funcional (functional), sistema (system), <em>performance</em>, carga (load) e testes de <em>stress</em>. Procure-os para mais informaçãos.</p>
</div>

<h3 id="O_que_o_Django_fornece_para_testes">O que o Django fornece para testes?</h3>

<p>Testar um website é uma tarefa complexa, porque isto é composto de várias camadas de lógica – do tratamento de requisições no nível HTTP, consultas de modelos, validação e processamento de formulários, e renderização de <em>template</em>.</p>

<p>Django fornece um <em>framework</em> de teste com uma baixa hierarquia de classes construida na biblioteca padrão <code><a href="https://docs.python.org/3/library/unittest.html#module-unittest" title="(in Python v3.5)">unittest</a></code> de Python. Apesar do nome, este <em>framework</em> de teste é adequado para testes unitários e de integração. O <em>framework</em> Django adiciona métodos e ferramentas de API para ajudar a testar o comportamento web e específico do Django. Isso permite você simular requisições, inserir dados de teste e inspecionar as saídas do seu aplicativo. Django também fornece uma API (<a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>) e ferramentas para <a href="https://docs.djangoproject.com/en/2.1/topics/testing/advanced/#other-testing-frameworks">usar diferentes frameworks de teste</a>, por exemplo, você pode integrar com o popular framework <a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> para simular um usuário interagindo com um navegador.</p>

<p>Para escrever um teste, você deriva de qualquer uma das classes base de teste de Django (ou <em>unittest</em>) (<a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#simpletestcase">SimpleTestCase</a><a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#transactiontestcase">TransactionTestCase</a><a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">TestCase</a><a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>) e então escreve métodos separados para verificar se a funcionalidade específica funciona como esperado (testes usam métodos "<em>assert</em>" para testar se a expressão resulta em valores <code>True</code> ou <code>False</code>, ou se os dois valores são iguais, etc.). Quando você inicia a execução de um teste, o framework executa os métodos de teste escolhidos em suas classes derivadas. Os métodos de teste são executados independentemente, com configuração comum e/ou comportamento <em>tear-down</em> definido na classe, como mostrado abaixo.</p>

<pre class="brush: python notranslate">class YourTestClass(TestCase):
    def setUp(self):
        # Setup run before every test method.
        pass

    def tearDown(self):
        # Clean up run after every test method.
        pass

    def test_something_that_will_pass(self):
        self.assertFalse(False)

    def test_something_that_will_fail(self):
        self.assertTrue(False)
</pre>

<p>A melhor classe base para maioria dos testes é <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">django.test.TestCase</a>. Esta classe de teste cria um banco de dados limpo antes dos testes serem executados, e executa todas as funções de teste em sua própria transação. A classe também possui um <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.Client" title="django.test.Client">Client</a> de teste, que você pode utilizar para simular um usuário interagindo com o código no nível de <em>view</em>. Nas seções a seguir vamos nos concentrar nos testes unitários, criados utilizando a classe base <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">TestCase</a>.</p>

<div class="note">
<p><strong>Nota:</strong> A classe <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">django.test.TestCase</a> é muito conveniente, mas pode resultar em alguns testes mais lentos do que necessitam ser (nem todo teste necessita configurar seu próprio banco de dados ou simular interação de <em>view</em>). Uma vez que esteja familiar com o que você pode fazer com essa classe, você pode querer substituir alguns dos seus testes por classes de teste mais simples disponíveis.</p>
</div>

<h3 id="O_que_você_deve_testar">O que você deve testar?</h3>

<p>Você deve testar todos aspectos do seu próprio código, mas nenhuma biblioteca ou funcionalidade oferecida como parte do Python ou Django.</p>

<p>Assim por exemplo, conseidere o <em>model</em> <code>Author</code> definido abaixo. Você não precisa testar explicitamente se <code>first_name</code> e <code>last_name</code> foram armazenados corretamente como <code>CharField</code> no banco de dados, porque isso é algo definido pelo Django (embora, é claro, na prática você inevitávelmente testará esta funcionalidade durante o desenvolvimento). Você também não precisa testar se o <code>date_of_birth</code> foi validado para ser um campo de data, porque isso novamente é algo implementeado no Django.</p>

<p>No entanto, você deve verificar o texto utilizado para os <em>labels</em> (<em>First name, Last name, Date of birth, Died</em>), e o tamanho do campo alocado para o texto (<em>100 caracteres</em>), porque isso faz parte do seu <em>design</em> e algo que pode ser violado/alterado no futuro.</p>

<pre class="brush: python notranslate">class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    def get_absolute_url(self):
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        return '%s, %s' % (self.last_name, self.first_name)</pre>

<p>Similarmente, você deve verificar se os métodos personalizados  <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> e <code style="font-style: normal; font-weight: normal;">__str__()</code> se comportam como desejado, porque els são sua lógica de código/negócios. No caso de <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> você pode confiar que o método <code>reverse()</code> de Django, foi implementado corretamente, portanto, o que você esta testando é se a <em>view</em> associada foi realmente definida.</p>

<div class="note">
<p><strong>Nota:</strong> Leitores astutos podem notar que também gostariamos de restringir que a data de nascimento e morte como valores sensíveis, e verificar se a morte vem após o nascimento. Em Django, esta restrição seria adicionada a suas classes <em>form</em> (Embora você possa definir validadores para campos do modelo e validadores de modelo, estes só serão usados no nível do formulário se forem chamdos pelo método clean() do model. Isso requer um ModelForm ou o método clean() do modelo precisa ser especificamente chamado).</p>
</div>

<p>Com isso em mente, vamos começar a ver como definir e executar testes.</p>

<h2 id="Visão_geral_da_estrutura_de_teste">Visão geral da estrutura de teste</h2>

<p>Antes de entrarmos nos detalhes de "o que testar", vamos primeiro examinar brevemente <em>onde</em> e <em>como</em> os testes são definidos.</p>

<p>Django usa o módulo <em>unittest</em> com <a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" title="(in Python v3.5)">descoberta de teste acoplada</a>, que descrobrirá testes no diretório de trabalho atual em qualquer arquivo nomeado com o padrão <strong>test*.py</strong>. Fornecido o nome do arquivo adequadamente, você pode usar qualquer estrutura que desejar. Recomendamos que você crie um módulo para seu código de teste, e tenha arquivos separados para <em>models</em>, <em>views</em>, <em>forms </em>e qualquer outro tipo de código que você precise testar. Por exemplo:</p>

<pre class="notranslate">catalog/
  /tests/
    __init__.py
    test_models.py
    test_forms.py
    test_views.py
</pre>

<p>Crie uma estrutura de arquivos como mostrado acima em seu projeto <em>LocalLibrary</em>. O <strong>__init__.py</strong> deve ser um arquivo vazio (isso informa ao Python que o diretório é um pacote). Você pode criar os três arquivos de teste copiando e renomeando o arquivo de teste do "esqueleto" <strong>/catalog/tests.py</strong>.</p>

<div class="note">
<p><strong>Nota:</strong> O arquivo de teste <strong>/catalog/tests.py</strong> do "esqueleto", foi criado automaticamente quando nós <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">construimos o "esqueleto" do website Django</a>. É perfeitamente "legal" colocar todos seus testes dentro dele, mas se você testar devidamente, você acabará rapidamente com um arquivo de teste muito grande e incontrolável.</p>

<p>Exclua o arquivo do "esqueleto", pois não precisamos dele.</p>
</div>

<p>Abra <strong>/catalog/tests/test_models.py</strong>. O arquivo deve importar <code>django.test.TestCase</code>, como mostrado:</p>

<pre class="brush: python notranslate">from django.test import TestCase

# Create your tests here.
</pre>

<p>Frequentemente, você adicionará uma classe de teste para cada <em>model/view/form</em> que deseja testar, com métodos individuais para testar funcionalidades específicas. Em outros casos, você pode desejar ter uma classe separada para testar um caso de uso específico, com funções de teste individuais que testam aspectos desse caso de uso (por exemplo, uma classe para testar se um campo do <em>model</em> é validado corretamente, com funções para testar cada um dos possíveis casos de falha). Novamente, a estrutura depende muito de você, mas é melhor se você for consistente.</p>

<p>Adicione a classe de teste abaixo na parte inferior do arquivo. A classe demonstra como construir uma classe de teste derivando de <code>TestCase</code>.</p>

<pre class="brush: python notranslate">class YourTestClass(TestCase):
    @classmethod
    def setUpTestData(cls):
        print("setUpTestData: Run once to set up non-modified data for all class methods.")
        pass

    def setUp(self):
        print("setUp: Run once for every test method to setup clean data.")
        pass

    def test_false_is_false(self):
        print("Method: test_false_is_false.")
        self.assertFalse(False)

    def test_false_is_true(self):
        print("Method: test_false_is_true.")
        self.assertTrue(False)

    def test_one_plus_one_equals_two(self):
        print("Method: test_one_plus_one_equals_two.")
        self.assertEqual(1 + 1, 2)</pre>

<p>A nova classe define dois métodos que você pode utilizar para aconfiguração de pré-teste (por exemplo, para criar quaisquer modelos ou outros objetos que precisará para to teste):</p>

<ul>
 <li><code>setUpTestData()</code> é chamado uma vez no início da execução do teste para configuração em nível de classe. Você usaria isso para criar objetos que não serão modificados ou alterados em nenhum dos métodos de teste.</li>
 <li><code>setUp()</code> é chamado antes de toda função de teste para configurar qualquer objeto que possa ser modificado pelo teste (toda função de teste receberá uma versão "nova" desses objetos).</li>
</ul>

<div class="note">
<p>As classes de teste também têm um método <code>tearDown()</code>, que não usamos. Este método não é particularmente útil para testes de banco de dados, pois a classe base <code>TestCase</code> cuida da desmontagem do banco de dados para você.</p>
</div>

<p>Abaixo desses, temos vários métodos de teste, que usam funções <code>Assert</code> para testar se as condições são verdadeiras, falsas ou iguais (<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>). Se a condição não for avaliada como esperado, então o teste falhará e reportará o erro ao seu console.</p>

<p><code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code> são assertivas padrão fornecidas pelo <strong>unittest</strong>. Existem outras assertivas padão no <em>framework</em> e também <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#assertions">Django especifica assertivas</a> para testar se uma <em>view</em> redireciona (<code>assertRedirects</code>), para testar se um template específico foi utilizado (<code>assertTemplateUsed</code>), etc.</p>

<div class="note">
<p>Você normalmente não deve incluir funções <strong>print()</strong> em seus testes como mostrado acima. Nós fizemos isso aqui apenas para que você posssa ver no console a ordem que as funções de configuração são chamadas (na seção a seguir).</p>
</div>

<h2 id="Como_executar_os_testes">Como executar os testes</h2>

<p>A maneira mais fácil para executar todos os testes é usar o comando:</p>

<pre class="brush: bash notranslate">python3 manage.py test</pre>

<p>Isso descobrirá todos arquivos nomeados com o padrão <strong>test*.py</strong> no diretório atual e executará todos testes definidos usando as classes base apropriadas (aqui temos vários arquivos de teste, mas, atualmente, apenas  <strong>/catalog/tests/test_models.py</strong> contém testes). Por padrão, os testes irão reportar individualmente apenas falhas no teste, seguidos por um resumo do teste.</p>

<div class="note">
<p>Se você obter erros semelhantes a: <code>ValueError: Missing staticfiles manifest entry ...</code> isso pode ocorrer porque o teste não é executado como <em>collectstatic</em> por padrão e seu <em>app</em> está usando uma classe de armazenamento que exige isto (veja <a href="https://docs.djangoproject.com/en/2.1/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage.manifest_strict">manifest_strict</a> para mais informações). Existem várias maneiras de solucionar esse problema - o mais fácil é simplesmente executar <em>collectstatic</em> antes de executar os testes:</p>

<pre class="brush: bash notranslate">python3 manage.py collectstatic
</pre>
</div>

<p>Execute os testes no diretório raiz de <em>LocalLibrary</em>. Você deve ver uma saída como a abaixo.</p>

<pre class="brush: bash notranslate">&gt; python3 manage.py test

Creating test database for alias 'default'...
<strong>setUpTestData: Run once to set up non-modified data for all class methods.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_false.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_true.
setUp: Run once for every test method to setup clean data.
Method: test_one_plus_one_equals_two.</strong>
.
======================================================================
FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
    self.assertTrue(False)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 3 tests in 0.075s

FAILED (failures=1)
Destroying test database for alias 'default'...</pre>

<p>Aqui vemos que tivemos uma falha no teste e podemos ver exatamente qual função falhou e por quê (essa falha é esperada, porque <code>False</code> não é <code>True</code>!).</p>

<div class="note">
<p><strong>Dica: </strong>A coisa mais importante para aprender com a saída do teste acima é que é muito mais valioso se você utilizar nomes descritivos/informativos para seus objetos e métodos.</p>
</div>

<p>O texto acima mostrado em <strong>negrito</strong> normalmente não apareceria na saída do teste (isso é gerado pelas funções <code>print()</code> em nossos teste). Isso mostra como o método  <code>setUpTestData()</code> é chamdo uma vez para classe e <code>setUp()</code> é chamado antes de cada método.</p>

<p>As próximas seções mostram como você pode executar testes específicos e como controlar quanta infromação os testes exibem.</p>

<h3 id="Mostrando_mais_informações_de_teste">Mostrando mais informações de teste</h3>

<p>Se você deseja obter mais informação sobre a execução do teste, você pode mudar  a verbosidade (<em>verbosity)</em>. Por exemplo, para listar os sucessos do teste, bem como as falhas (e um monte de informações sobre como o banco de dados de teste está configurado) vocêpode definir a <em>verbosity</em> para "2" como mostrado:</p>

<pre class="brush: bash notranslate">python3 manage.py test --verbosity 2</pre>

<p>Os níveis permitidos de <em>verbosity</em> são 0, 1, 2, e 3, com o padrão sendo "1".</p>

<h3 id="Executando_testes_específicos">Executando testes específicos</h3>

<p>Se você desseja executar um subconjunto de seus testes, você pode fazer isso especificando o caminho completo para o(s) pacote(s), módulos, subclasse <code>TestCase</code> ou método:</p>

<pre class="brush: bash notranslate"># Run the specified module
python3 manage.py test catalog.tests

# Run the specified module
python3 manage.py test catalog.tests.test_models

# Run the specified class
python3 manage.py test catalog.tests.test_models.YourTestClass

# Run the specified method
python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two
</pre>

<h2 id="Testes_da_LocalLibrary">Testes da LocalLibrary</h2>

<p>Agora que sabemos como executar nosso testes e que tipo de coisas precisams testar, vamos ver alguns exemplos práticos.</p>

<div class="note">
<p><strong>Nota: </strong>Não escreveremos todos os testes possíveis, mas isso deve lhe dar uma ideia de como testes trabalham e o que mais você pode fazer.</p>
</div>

<h3 id="Models">Models</h3>

<p>Como discutido acima, devemos testar qualquer coisa que faça parte do nosso projeto ou que seja definido por código que escrevemos, mas não bibliotecas/códigos que já foram testados pelo Django ou pela equipe de desenvolvimento do Python.</p>

<p>Por exemplo, considere o <em>model</em> <code>Author</code> abaixo. Aqui devemos testar os <em>labels</em> para todos os campos, porque, embora não tenhamos específicado explicitamente a maioria deles, temos um projeto que diz quais devem ser esses valores. Se não testamos os valores, não sabemos se os <em>labels</em> dos campos  têm os valores pretendidos. Similarmente, enquanto confiamos que o Django criará um campo com o tamanho específicado, vale a pena específicar um teste para este tamanho, para garantir que ele foi implementado como planejado.</p>

<pre class="brush: python notranslate">class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    def get_absolute_url(self):
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        return f'{self.last_name}, {self.first_name}'</pre>

<p>Abra nosso <strong>/catalog/tests/test_models.py</strong>, e substitua qualquer código existente pelo seguinte código de teste para o <em>model</em> <code>Author</code>.</p>

<p>Aqui você verá que primeiro importamos <code>TestCase</code> e derivamos nossa classe de teste (<code>AuthorModelTest</code>) a partir dela, usando um nome descritivo para que possamos identificar facilmente quaiquer testes com falha na saída do teste. Nós então chamamos <code>setUpTestData()</code> para criar um objeto autor que iremos usar mas não modificaremos em nenhum dos testes.</p>

<pre class="brush: python notranslate">from django.test import TestCase

from catalog.models import Author

class AuthorModelTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # Set up non-modified objects used by all test methods
        Author.objects.create(first_name='Big', last_name='Bob')

    def test_first_name_label(self):
        author = Author.objects.get(id=1)
        field_label = author._meta.get_field('first_name').verbose_name
        self.assertEquals(field_label, 'first name')

    def test_date_of_death_label(self):
        author=Author.objects.get(id=1)
        field_label = author._meta.get_field('date_of_death').verbose_name
        self.assertEquals(field_label, 'died')

    def test_first_name_max_length(self):
        author = Author.objects.get(id=1)
        max_length = author._meta.get_field('first_name').max_length
        self.assertEquals(max_length, 100)

    def test_object_name_is_last_name_comma_first_name(self):
        author = Author.objects.get(id=1)
        expected_object_name = f'{author.last_name}, {author.first_name}'
        self.assertEquals(expected_object_name, str(author))

    def test_get_absolute_url(self):
        author = Author.objects.get(id=1)
        # This will also fail if the urlconf is not defined.
        self.assertEquals(author.get_absolute_url(), '/catalog/author/1')</pre>

<p>Os testes de campo verificam se os valores dos <em>labels</em> dos campos (<code>verbose_name</code>)  e se o tamanho dos campos de caracteres são como esperado. Todos esses métodos possuem nomes descritivos e seguem o mesmo padrão:</p>

<pre class="brush: python notranslate"># Get an author object to test
author = Author.objects.get(id=1)

# Get the metadata for the required field and use it to query the required field data
field_label = author._meta.get_field('first_name').verbose_name

# Compare the value to the expected result
self.assertEquals(field_label, 'first name')</pre>

<p>As coisas interessantes a serem observadas aqui:</p>

<ul>
 <li>Não podemos obter <code>verbose_name</code> diretamente utilizando  <code>author.first_name.verbose_name</code>, porque <code>author.first_name</code> é uma <em>string</em> (não um identificador para o objeto <code>first_name</code> que podemos utilizar para acessar suas propriedades). Em vez disso, precisamos utilizar o atributo <code>_meta</code> de <em>author</em> para obter uma instância do campo e usá-la para consultar informações adicionais.</li>
 <li>Optamos por utilizar <code>assertEquals(field_label,'first name')</code> em vez de <code>assertTrue(field_label == 'first name')</code>. A razão para isso é que, se o teste falhar a saída do primeiro informa o que realmente era o <em>label</em>, que torna a depuração do problema um pouco mais fácil.</li>
</ul>

<div class="note">
<p><strong>Nota:</strong> Testes para os rótulos <code>last_name</code> e <code>date_of_birth</code> e também para o teste para o tamanho do <code>last_name</code> field foram omitidos. Adicione suas próprias versões agora, seguindo as convenções de nomeclatura e abordagens mostradas acima.</p>
</div>

<p>Também precisamos testar nossos métodos personalizados. Eles, essencialmente, apenas verificam se o nome do objeto foi construido como esperamos, usando o formato "Last Name", "First Name", e se a URL que obtemos para um item de <code>Author</code> é o que esperávamos.</p>

<pre class="brush: python notranslate">def test_object_name_is_last_name_comma_first_name(self):
    author = Author.objects.get(id=1)
    expected_object_name = f'{author.last_name}, {author.first_name}'
    self.assertEquals(expected_object_name, str(author))

def test_get_absolute_url(self):
    author = Author.objects.get(id=1)
    # This will also fail if the urlconf is not defined.
    self.assertEquals(author.get_absolute_url(), '/catalog/author/1')</pre>

<p>Execute os testes agora. Se você criou o modelo Author como descrevemos no tutorial de modelos, é bem provável que você obtenha um erro para o <em>label</em> <code>date_of_death</code> como mostrado abaixo. O teste está falhando porque foi escrito esperando que a definição do <em>label</em> siga a convenção do Django de não colocar em maíúscula a primeira letra do <em>label</em> (Django faz isso por você).</p>

<pre class="brush: bash notranslate">======================================================================
FAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\...\locallibrary\catalog\tests\test_models.py", line 32, in test_date_of_death_label
    self.assertEquals(field_label,'died')
AssertionError: 'Died' != 'died'
- Died
? ^
+ died
? ^</pre>

<p>Este é um bug muito pequeno, mas destaca como a escrita de testes pode verificar mais minuciosamente quaislquer suposições que você tenha feito.</p>

<div class="note">
<p><strong>Nota:</strong> Altere o <em>label</em> para o campo date_of_death (/catalog/models.py) para "died" e re-executes os testes.</p>
</div>

<p>Os padrões para testar os outros modelos são semelhantes, portanto não continuaremos discutindo mais isso. Sinta-se livre para criar seus próprios testes para nossos outros modelos.</p>

<h3 id="Forms">Forms</h3>

<p>A filosofia para testar seus <em>forms </em>é a mesma que para testar seus <em>models</em>; você precisa testar qualquer coisa que tenha codificado ou seu projeto especifica, mas não o comportamento do framework subjacente e outras bibliotecas de terceiros</p>

<p>Geralmente, isso significa que você deve testar se os <em>forms</em> têm os campos que você deseja e se esses são exibidos com os <em>labels</em>  e texto de ajuda apropriados. Você não precisa verificar se o Django o tipo de campo corretamente (a menos que você tenha criado seu próprio campo e validação personalizados) — ex. você não precisa testar se um campo de email aceita apenas email. No entanto,  você precisaria testar qualquer validação adicional que você espera que seja executada nos campos e quaisquer mensagens que seu código irá gerar para erros.</p>

<p>Considere nosso <em>form</em> para renovação de livros. Ele tem apenas um campo para data de renovação, que terá um <em>label</em> e um texto de ajuda que precisaremos verificar.</p>

<pre class="brush: python notranslate">class RenewBookForm(forms.Form):
    """Form for a librarian to renew books."""
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

    def clean_renewal_date(self):
        data = self.cleaned_data['renewal_date']

        # Check if a date is not in the past.
        if data &lt; datetime.date.today():
            raise ValidationError(_('Invalid date - renewal in past'))

        # Check if date is in the allowed range (+4 weeks from today).
        if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))

        # Remember to always return the cleaned data.
        return data</pre>

<p>Abra nosso arquivo <strong>/catalog/tests/test_forms.py</strong> e substitua qualquer código existente pelo seguinte código de teste para o <em>form</em> <code>RenewBookForm</code>. Nós iniciamos importando nosso <em>form</em> e algumas bibliotecas Python e Django para ajudar testar funcionalidades relacionadas ao tempo. Em seguida, declaramos nossa classe de teste do <em>form,</em> da mesma maneira que fizemos para <em>models</em>,  usando um nome descritivo para a classe de teste derivada de <code>TestCase</code>.</p>

<pre class="brush: python notranslate">import datetime

from django.test import TestCase
from django.utils import timezone

from catalog.forms import RenewBookForm

class RenewBookFormTest(TestCase):
    def test_renew_form_date_field_label(self):
        form = RenewBookForm()
        self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')

    def test_renew_form_date_field_help_text(self):
        form = RenewBookForm()
        self.assertEqual(form.fields['renewal_date'].help_text, 'Enter a date between now and 4 weeks (default 3).')

    def test_renew_form_date_in_past(self):
        date = datetime.date.today() - datetime.timedelta(days=1)
        form = RenewBookForm(data={'renewal_date': date})
        self.assertFalse(form.is_valid())

    def test_renew_form_date_too_far_in_future(self):
        date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
        form = RenewBookForm(data={'renewal_date': date})
        self.assertFalse(form.is_valid())

    def test_renew_form_date_today(self):
        date = datetime.date.today()
        form = RenewBookForm(data={'renewal_date': date})
        self.assertTrue(form.is_valid())

    def test_renew_form_date_max(self):
        date = timezone.localtime() + datetime.timedelta(weeks=4)
        form = RenewBookForm(data={'renewal_date': date})
        self.assertTrue(form.is_valid())
</pre>

<p>As primeiras duas funções testam se os campos <code>label</code> e <code>help_text</code> são como esperados. Temos que acessar o campo usando o dicionário de campos (ex. <code>form.fields['renewal_date']</code>). Observe aqui que também precisamos testar se o valor do <em>label</em> é <code>None</code>, porque mesmo que o Django processe o <em>label</em> correto, retornará <code>None</code> se o valor não estiver definido explicitamente.</p>

<p>O restante das funções testam se o form é valido para datas de renovação dentro do intervalo aceitável e inválido para os valores foram do intervalo. Observe como construimos os valores teste de data em torno de nossa data atual (<code>datetime.date.today()</code>) usando <code>datetime.timedelta()</code> (nesse caso, especificando um número de dias ou semanas). Então, apenas criamos o <em>form</em>, passando nossos dados e testando se é válido.</p>

<div class="note">
<p><strong>Nota:</strong> Aqui, na realidade, não usamos o banco de dados ou cliente teste. Considere modificar essses testes para utilizar <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.SimpleTestCase">SimpleTestCase</a>.</p>

<p>Também precisamos validar que os erros corretos sejam gerados se o form é inválido, no entanto, isso geralmente é feito no processamento da view, portanto trataremos disso na próxima seção.</p>
</div>

<p>Isso é tudo para <em>forms</em>; nós temos alguns outros, mas eles são automaticamente criados pelas nossas <em>views</em> de edição baseada na classe genérica, e devem ser testadas lá! Execute os testes e confirme  se nosso código ainda passa!</p>

<h3 id="Views">Views</h3>

<p>Para validar o comportamento das nossas <em>views</em>, utilzamos <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.Client">Client</a> de teste do Django. Essa classe funciona como um navegador web fictício que podemos usar para simular requisições <code>GET</code> and <code>POST</code> em uma URL e observar a resposta. Podemos ver quase tudo sobre a resposta, desde HTTP de baixo nível (cabeçalhos de resultados e códigos de status) até o <em>template</em> que estamos utilizando para renderizar o HTML e os dados de contexto que estamos passando para ele. Também podemos ver a cadeia de redirecionamentos (se houver) e verificar a URL e o código de status em cada etapa. Isso nos permite verificar se cada <em>view</em> esta fazendo o que é esperado.</p>

<p>Vamos iniciar com uma de nossas <em>views</em> mais simples, que fornece uma lista de todos Autores. Isso é exibido na URL <strong>/catalog/authors/</strong> (uma URL chamada 'authors' na configuração de URL).</p>

<pre class="brush: python notranslate">class AuthorListView(generic.ListView):
    model = Author
    paginate_by = 10
</pre>

<p>Como esta é uma <em>list view</em> genérica, quase tudo é feito para nós pelo Django. Provavelmente, se você confia no Django, então a única coisa que você precisa testar é se a <em>view</em> é acessível na URL correta e pode ser acessada usando seu nome. No entanto, se você está usando um desenvolvimento orientado a testes, você iniciará escrevendo testes que confirmam que a <em>view</em> exibe todos Autores, paginando-os em lotes de 10.</p>

<p>Abra o arquivo <strong>/catalog/tests/test_views.py</strong> e substitua qualquer texto existente pelo seguinte código de teste para <code>AuthorListView</code>. Como antes, importamos nosso <em>model</em> e algumas classe úteis. No método <code>setUpTestData()</code> configuramos vários objetos <code>Author</code> para que possamos testar nossa paginação.</p>

<pre class="brush: python notranslate">from django.test import TestCase
from django.urls import reverse

from catalog.models import Author

class AuthorListViewTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # Create 13 authors for pagination tests
        number_of_authors = 13

        for author_id in range(number_of_authors):
            Author.objects.create(
                first_name=f'Christian {author_id}',
                last_name=f'Surname {author_id}',
            )

    def test_view_url_exists_at_desired_location(self):
        response = self.client.get('/catalog/authors/')
        self.assertEqual(response.status_code, 200)

    def test_view_url_accessible_by_name(self):
        response = self.client.get(reverse('authors'))
        self.assertEqual(response.status_code, 200)

    def test_view_uses_correct_template(self):
        response = self.client.get(reverse('authors'))
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'catalog/author_list.html')

    def test_pagination_is_ten(self):
        response = self.client.get(reverse('authors'))
        self.assertEqual(response.status_code, 200)
        self.assertTrue('is_paginated' in response.context)
        self.assertTrue(response.context['is_paginated'] == True)
        self.assertTrue(len(response.context['author_list']) == 10)

    def test_lists_all_authors(self):
        # Get second page and confirm it has (exactly) remaining 3 items
        response = self.client.get(reverse('authors')+'?page=2')
        self.assertEqual(response.status_code, 200)
        self.assertTrue('is_paginated' in response.context)
        self.assertTrue(response.context['is_paginated'] == True)
        self.assertTrue(len(response.context['author_list']) == 3)</pre>

<p>Todos os teste usam o cliente (pertenecente a nossa classe derivada <code>TestCase</code>'s) para simular uma requisição <code>GET</code> e obter uma resposta. A primeira versão verifica uma URL específica URL (observe, apenas o caminho específico, sem o domínio), enquanto a segunda gera a URL  a partir do seu nome na configuração da URL.</p>

<pre class="brush: python notranslate">response = self.client.get('/catalog/authors/')
response = self.client.get(reverse('authors'))
</pre>

<p>Uma vez que temos a resposta, consultamos o seu código de status, o <em>template</em> usado, se a resposta é paginada ou não, o número de itens retonado e o número total de itens.</p>

<div class="blockIndicator note">
<p class="brush: python">Nota: Se você definir a variável <code>paginate_by</code> em seu arquivo  <strong>/c</strong><strong>atalog/views.py</strong> para um número diferente de 10, atualize as linhas que testam se o número correto de itens é exibido nos <em>templates</em> paginados acima e nas seções seguintes. Por exemplo, se você definiu a variável para a lista de autor para 5, atualize a linha acima para:</p>

<pre class="brush: python notranslate">self.assertTrue(len(response.context['author_list']) == 5)
</pre>
</div>

<p>A variável mais importante que demonstramos acima é <code>response.context</code>, que é a variável de contexto passada para o <em>template</em> pela <em>view</em>. Isso é incrivelmente útil para testes, porque permite confirmar que nosso template está obtendo todos os dados necessários. Em outras palavras, podemos verificar se estamos utilizando o <em>template</em> pretendido e quais dados o <em>template</em> está obtendo, o que ajuda bastante a verificar que alguns problemas de renderização são apenas devido ao <em>template</em>.</p>

<h4 id="Views_restritas_a_usuários_logados"><em>Views</em> restritas a usuários logados</h4>

<p>Em alguns casos, você desejará testar uma <em>view</em> que é restrita apenas aos usuários logados. Por exemplo, nossa <code>LoanedBooksByUserListView</code> é muito similar a nossa <em>view</em> anterior, mas está disponível apenas para usuários logados e exibe apenas os registros <code>BookInstance</code> que são emprestados pelo usuário atual, têm o status 'emprestado' e são ordenados "mais antigos primeiro".</p>

<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin

class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):
    """Generic class-based view listing books on loan to current user."""
    model = BookInstance
    template_name ='catalog/bookinstance_list_borrowed_user.html'
    paginate_by = 10

    def get_queryset(self):
        return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>

<p>Adicione o código seguinte ao <strong>/catalog/tests/test_views.py</strong>. Aqui, primeiro usamos <code>SetUp()</code> para criar alguma contas de login de usuário e objetos <code>BookInstance</code> (junto com seus livros associados e outros registros) que usaremos posteriormente nos testes. Metade dos livros são emprestados para cada usuário teste, mas inicialmente definimos o status de todos os livros como  "manutenção". Usamos <code>SetUp()</code> em vez de <code>setUpTestData()</code> porque modificaremos alguns desses objetos depois.</p>

<div class="note">
<p><strong>Nota:</strong> O código <code>setUp()</code> abaixo, cria um livro com uma  <code>Language</code> especificada, mas seu código pode não incluir o <em>model</em> <code>Language</code>, pois foi criado como um desafio. Se esse for o caso, simplesmente comente as partes do código que cria ou importa objetos <em>Language</em>. Você também deve fazer isso na seção  <code>RenewBookInstancesViewTest</code> a seguir.</p>
</div>

<pre class="brush: python notranslate">import datetime

from django.utils import timezone
from django.contrib.auth.models import User # Required to assign User as a borrower

from catalog.models import BookInstance, Book, Genre, Language

class LoanedBookInstancesByUserListViewTest(TestCase):
    def setUp(self):
        # Create two users
        test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')
        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')

        test_user1.save()
        test_user2.save()

        # Create a book
        test_author = Author.objects.create(first_name='John', last_name='Smith')
        test_genre = Genre.objects.create(name='Fantasy')
        test_language = Language.objects.create(name='English')
        test_book = Book.objects.create(
            title='Book Title',
            summary='My book summary',
            isbn='ABCDEFG',
            author=test_author,
            language=test_language,
        )

        # Create genre as a post-step
        genre_objects_for_book = Genre.objects.all()
        test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
        test_book.save()

        # Create 30 BookInstance objects
        number_of_book_copies = 30
        for book_copy in range(number_of_book_copies):
            return_date = timezone.localtime() + datetime.timedelta(days=book_copy%5)
            the_borrower = test_user1 if book_copy % 2 else test_user2
            status = 'm'
            BookInstance.objects.create(
                book=test_book,
                imprint='Unlikely Imprint, 2016',
                due_back=return_date,
                borrower=the_borrower,
                status=status,
            )

    def test_redirect_if_not_logged_in(self):
        response = self.client.get(reverse('my-borrowed'))
        self.assertRedirects(response, '/accounts/login/?next=/catalog/mybooks/')

    def test_logged_in_uses_correct_template(self):
        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
        response = self.client.get(reverse('my-borrowed'))

        # Check our user is logged in
        self.assertEqual(str(response.context['user']), 'testuser1')
        # Check that we got a response "success"
        self.assertEqual(response.status_code, 200)

        # Check we used correct template
        self.assertTemplateUsed(response, 'catalog/bookinstance_list_borrowed_user.html')
</pre>

<p>Para verificar se a <em>view</em> será redirecionada para uma página de login se o usuário não estiver logado, usamos <code>assertRedirects</code>, como demonstrado em <code>test_redirect_if_not_logged_in()</code>. Para verificar se a página é exibida para um usuário logado, primeiro logamos com nosso usuário teste e então acessamos a página novamente e verificamos se obtivemos um <code>status_code</code> de 200 (successo). </p>

<p>O restante dos testes verificam se nossa <em>view</em> retorna apenas livros emprestados ao nosso usuário atual. Copie o código abaixo e cole no final da classe de teste acima.</p>

<pre class="brush: python notranslate">    def test_only_borrowed_books_in_list(self):
        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
        response = self.client.get(reverse('my-borrowed'))

        # Check our user is logged in
        self.assertEqual(str(response.context['user']), 'testuser1')
        # Check that we got a response "success"
        self.assertEqual(response.status_code, 200)

        # Check that initially we don't have any books in list (none on loan)
        self.assertTrue('bookinstance_list' in response.context)
        self.assertEqual(len(response.context['bookinstance_list']), 0)

        # Now change all books to be on loan
        books = BookInstance.objects.all()[:10]

        for book in books:
            book.status = 'o'
            book.save()

        # Check that now we have borrowed books in the list
        response = self.client.get(reverse('my-borrowed'))
        # Check our user is logged in
        self.assertEqual(str(response.context['user']), 'testuser1')
        # Check that we got a response "success"
        self.assertEqual(response.status_code, 200)

        self.assertTrue('bookinstance_list' in response.context)

        # Confirm all books belong to testuser1 and are on loan
        for bookitem in response.context['bookinstance_list']:
            self.assertEqual(response.context['user'], bookitem.borrower)
            self.assertEqual('o', bookitem.status)

    def test_pages_ordered_by_due_date(self):
        # Change all books to be on loan
        for book in BookInstance.objects.all():
            book.status='o'
            book.save()

        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
        response = self.client.get(reverse('my-borrowed'))

        # Check our user is logged in
        self.assertEqual(str(response.context['user']), 'testuser1')
        # Check that we got a response "success"
        self.assertEqual(response.status_code, 200)

        # Confirm that of the items, only 10 are displayed due to pagination.
        self.assertEqual(len(response.context['bookinstance_list']), 10)

        last_date = 0
        for book in response.context['bookinstance_list']:
            if last_date == 0:
                last_date = book.due_back
            else:
                self.assertTrue(last_date &lt;= book.due_back)
                last_date = book.due_back</pre>

<p>Você também pode adicionar testes de paginação, se desejar!</p>

<h4 id="Testando_views_com_forms">Testando <em>views</em> com <em>forms</em></h4>

<p>Testar views com forms é um pouco mais complicado que nos casos acima, porque você precisa testar mais caminhos de código: exibição inicial, exibição após falha de validação de dados e exibição após validação com sucesso. A boa notícia é que usamos o cliente para testar quase exatamente da mesma maneira que fizemos para <em>views</em> somente de exibição.</p>

<p>Para demonstrar, vamos escrever alguns testes para a <em>view</em> usada para renovar livros (<code>renew_book_librarian()</code>):</p>

<pre class="brush: python notranslate">from catalog.forms import RenewBookForm

@permission_required('catalog.can_mark_returned')
def renew_book_librarian(request, pk):
    """View function for renewing a specific BookInstance by librarian."""
    book_instance = get_object_or_404(BookInstance, pk=pk)

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding):
        book_renewal_form = RenewBookForm(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_instance.due_back = form.cleaned_data['renewal_date']
            book_instance.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed'))

    # If this is a GET (or any other method) create the default form
    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        book_renewal_form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})

    context = {
        'book_renewal_form': book_renewal_form,
        'book_instance': book_instance,
    }

    return render(request, 'catalog/book_renew_librarian.html', context)</pre>

<p>Precisamos testar se a <em>view</em> está disponível apenas para usuários que têm a permissão <code>can_mark_returned </code>, e se eles são direcionados para uma página de erro HTTP 404 se tentarem renovar um <code>BookInstance</code> que não existe. Devemos verificar se o valor inicial do form é propagado com uma data três semanas no futuro e se a validação for bem sucedida somos redirecionados para a <em>view</em> "all-borrowed books". Como parte da verificação dos testes de falha de validação, também verificaremos se nosso <em>form</em> está enviando mensagens de erro apropriadas.</p>

<p>Adicione a primeira parte da classe de teste (mostrada abaixo) na parte inferior de <strong>/catalog/tests/test_views.py</strong>. Isso cria dois usuários e duas instâncias de livro, mas apenas concede a um usuário a permissão necessária para acessar a <em>view</em>. O código para conceder permissões durante os testes é mostrado em negrito:</p>

<pre class="brush: python notranslate">import uuid

from django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned.

class RenewBookInstancesViewTest(TestCase):
    def setUp(self):
        # Create a user
        test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')
        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')

        test_user1.save()
        test_user2.save()

<strong>        permission = Permission.objects.get(name='Set book as returned')
        test_user2.user_permissions.add(permission)
        test_user2.save()</strong>

        # Create a book
        test_author = Author.objects.create(first_name='John', last_name='Smith')
        test_genre = Genre.objects.create(name='Fantasy')
        test_language = Language.objects.create(name='English')
        test_book = Book.objects.create(
            title='Book Title',
            summary='My book summary',
            isbn='ABCDEFG',
            author=test_author,
            language=test_language,
        )

        # Create genre as a post-step
        genre_objects_for_book = Genre.objects.all()
        test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
        test_book.save()

        # Create a BookInstance object for test_user1
        return_date = datetime.date.today() + datetime.timedelta(days=5)
        self.test_bookinstance1 = BookInstance.objects.create(
            book=test_book,
            imprint='Unlikely Imprint, 2016',
            due_back=return_date,
            borrower=test_user1,
            status='o',
        )

        # Create a BookInstance object for test_user2
        return_date = datetime.date.today() + datetime.timedelta(days=5)
        self.test_bookinstance2 = BookInstance.objects.create(
            book=test_book,
            imprint='Unlikely Imprint, 2016',
            due_back=return_date,
            borrower=test_user2,
            status='o',
        )</pre>

<p>Adicione os seguintes testes na parte inferior da classe de teste. Eles verificam se apenas usuários com a permissão correta (testuser2) podem aceesar a <em>view</em>. Verificamos todos os casos: quando o usuários não está logado, quando um usuário está logado mas não tem as permissões corretas, quando o usuário possui permissões, mas não é o tomador do empréstimo (deve ter êxito), e o que acontece quando eles tentam acessar uma <code>BookInstance</code> que não existe. Também verificamos se o <em>template</em> correto é utilizado.</p>

<pre class="brush: python notranslate">   def test_redirect_if_not_logged_in(self):
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
        # Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
        self.assertEqual(response.status_code, 302)
        self.assertTrue(response.url.startswith('/accounts/login/'))

    def test_redirect_if_logged_in_but_not_correct_permission(self):
        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
        self.assertEqual(response.status_code, 403)

    def test_logged_in_with_permission_borrowed_book(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance2.pk}))

        # Check that it lets us login - this is our book and we have the right permissions.
        self.assertEqual(response.status_code, 200)

    def test_logged_in_with_permission_another_users_borrowed_book(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))

        # Check that it lets us login. We're a librarian, so we can view any users book
        self.assertEqual(response.status_code, 200)

    def test_HTTP404_for_invalid_book_if_logged_in(self):
        # unlikely UID to match our bookinstance!
        test_uid = uuid.uuid4()
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid}))
        self.assertEqual(response.status_code, 404)

    def test_uses_correct_template(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
        self.assertEqual(response.status_code, 200)

        # Check we used correct template
        self.assertTemplateUsed(response, 'catalog/book_renew_librarian.html')
</pre>

<p>Adicione o próximo método de teste, como mostrado abaixo. Isso verifica se a data inicial para o form é três semanas no futuro. Observe como podemos acessar o valor do valor inicial do campo do form (mostrado em negrito).</p>

<pre class="brush: python notranslate">    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
        self.assertEqual(response.status_code, 200)

        date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3)
        self.assertEqual(response<strong>.context['form'].initial['renewal_date']</strong>, date_3_weeks_in_future)
</pre>

<div class="warning">
<p>Se você usar a classe <em>form</em> <code>RenewBookModelForm(forms.ModelForm)</code> em vez da classe <code>RenewBookForm(forms.Form)</code>, então o nome do campo do <em>form</em> será <strong>'due_back' </strong>em vez de <strong>'renewal_date'</strong>.</p>
</div>

<p>O próximo teste (adicione isso a classe também) verifica se a <em>view</em> redireciona para uma lista de todos livros emprestados, se a renovação for bem-sucedida. O que difere aqui é que pela primeira vez mostramos como você pode fazer <code>POST</code> de dados usando o cliente. Os dados do <em>post</em> são o segundo argumento da função <em>post</em>, e são especificados como um dicionário de chave/valores.</p>

<pre class="brush: python notranslate">    def test_redirects_to_all_borrowed_book_list_on_success(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
        <strong>response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future})</strong>
        self.assertRedirects(response, reverse('all-borrowed'))
</pre>

<div class="warning">
<p>A view <em>all-borrowed</em> foi adicionada como um <em>desafio</em>, e seu código pode, em vez disso, direcionar para a página inicial '/'. Nesse caso, modifique as últimas duas linhas do código de teste para que sejam como o código abaixo. O <code>follow=True</code> na solicitação, garante que a solicitação retorna a URL final de destino (portanto verifique <code>/catalog/</code> em vez de <code>/</code>).</p>

<pre class="brush: python notranslate"> response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, <strong>follow=True</strong> )
 <strong>self.assertRedirects(</strong><strong>response, '/catalog/')</strong></pre>
</div>

<p>Copie as última duas funções para a classe, como visto abaixo. Elas testam novamente as requisições <code>POST</code>, mas nesse caso, com datas inválidas de renovação. Utilizamos <code>assertFormError()</code> para verificar se as mensagens de erro são as esperadas.</p>

<pre class="brush: python notranslate">    def test_form_invalid_renewal_date_past(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        date_in_past = datetime.date.today() - datetime.timedelta(weeks=1)
        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': date_in_past})
        self.assertEqual(response.status_code, 200)
        <strong>self.assertFormError(</strong><strong>response, 'form', 'renewal_date', 'Invalid date - renewal in past')</strong>

    def test_form_invalid_renewal_date_future(self):
        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
        invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5)
        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': invalid_date_in_future})
        self.assertEqual(response.status_code, 200)
        <strong>self.assertFormError(</strong><strong>response, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')</strong>
</pre>

<p>Os mesmos tipos de técnicas podem ser usadas para testar a outra <em>view</em>.</p>

<h3 id="Templates"><em>Templates</em></h3>

<p>Django fornece APIs de teste para verificar se o template correto esta sendo chamado por suas views, e para permitir que você verifique se a informação correta está sendo enviada. Entretanto, não há suporte específico à API para testar no Django que sua saída HTML seja renderizada conforme esperado.</p>

<h2 id="Outras_ferramentas_de_teste_recomendadas">Outras ferramentas de teste recomendadas</h2>

<p>O framework de teste do Django pode ajudar você a escrever eficazes testes unitários e de integração — nós apenas arranhamos a superfície do que o framework <strong>unittest</strong> pode fazer, muito menos as adições de Django (por exemplo, confira como você pode usar <a href="https://docs.python.org/3.5/library/unittest.mock-examples.html">unittest.mock</a> para corrigir bibliotecas de terceiros para que você possa testar mais detalhadamente seu próprio código).</p>

<p>Embora existam inúmeras outras ferramentas de teste que você pode utilizar, destacaremos apenas duas:</p>

<ul>
 <li><a href="http://coverage.readthedocs.io/en/latest/">Coverage</a>: Essa ferramenta Python reporta quando do seu código é realmente executado pelos seus testes. É particularmente útil quando você começando e está tentando descobrir o que exatamente deve testar.</li>
 <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> é um framework para automatizar testes em um navegador real. Ele permite simular um usuário real interagindo com o site e fornece uma excelente estrutura para o sistema testar seu site (a próxima etapa do teste de integração).</li>
</ul>

<h2 id="Desafie-se">Desafie-se</h2>

<p>Existem muito mais <em>models</em> e <em>views</em> que podemos testar. Como uma tarefa simples, tente criar um caso de teste para a <em>view</em> <code>AuthorCreate</code>.</p>

<pre class="brush: python notranslate">class AuthorCreate(PermissionRequiredMixin, CreateView):
    model = Author
    fields = '__all__'
    initial = {'date_of_death':'12/10/2016'}
    permission_required = 'catalog.can_mark_returned'</pre>

<p>Lembre-se de que você precisa verificar qualquer coisa que você especificar ou que faça parte do projeto. Isso incluirá quem tem acesso, a data inicial, o <em>template</em> utilizado e para onde a view é redirecionada quando bem-sucedida.</p>

<h2 id="Resumo">Resumo</h2>

<p>Escrever código de teste não é divertido nem glamuroso, e é consequentemente muitas vezes deixado por último (ou nem isso) ao criar um site. No entanto, é uma parte essencial para garantir que seu código esteja seguro para <em>release</em> após fazer alterações e de baixo custo de manutenção.</p>

<p>Neste tutorial, mostramos como escrever e executar testes para seus <em>models</em>, <em>forms</em> e <em>views</em>. Mais importante ainda, fornecemos um breve resumo do que você deve testar, que geralmente é a coisa mais difícil de resolver quando você está iniciando. Há muito mais para conhecer, mas mesmo com o que você já aprendeu, poderá criar testes unitários eficazes para seus websites.</p>

<p>O próximo e último tutorial mostra como você pode implantar seu maravilhoso (e totalmente testado!) website Django.</p>

<h2 id="Veja_também">Veja também</h2>

<ul>
 <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/overview/">Escrevendo e executando testes</a> (Django docs)</li>
 <li><a href="https://docs.djangoproject.com/en/2.1/intro/tutorial05/">Escrevendo seu primeiro app Django, parte 5 &gt; Introduzindo testes automatizados</a> (Django docs)</li>
 <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/">Refererências de ferramentas de teste</a> (Django docs)</li>
 <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/advanced/">Tópicos avançados de testes</a> (Django docs)</li>
 <li><a href="http://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/">Um Guia para testes no Django</a> (Toast Driven Blog, 2011)</li>
 <li><a href="http://test-driven-django-development.readthedocs.io/en/latest/index.html">Workshop: Desenvolvimento web Orientado a Testes com Django</a> (San Diego Python, 2014)</li>
 <li><a href="https://realpython.com/blog/python/testing-in-django-part-1-best-practices-and-examples/">Testando no Django (Parte 1) - Melhores práticas e Exemplos</a> (RealPython, 2013)</li>
</ul>

<p>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</p>

<h2 id="Neste_módulo">Neste módulo</h2>

<ul>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Introduction">Introdução ao Django</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/development_environment">Configurando um ambiente de desenvolvimento Django</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial Django: Website de uma Biblioteca Local</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/skeleton_website">Tutorial Django Parte 2: Criando a base do website</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Models">Tutorial Django Parte 3: Usando models</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Admin_site">Tutorial Django Parte 4: Django admin site</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Home_page">Tutorial Django Parte 5: Criando nossa página principal</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Generic_views">Tutorial Django Parte 6: Lista genérica e detail views</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Sessions">Tutorial Django Parte 7: Framework de Sessões</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Authentication">Tutorial Django Parte 8: Autenticação de Usuário e permissões</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Forms">Tutorial Django Parte 9: Trabalhando com formulários</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Testing">Tutorial Django Parte 10: Testando uma aplicação web Django</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/Deployment">Tutorial Django Parte 11: Implantando Django em produção</a></li>
 <li><a rel="nofollow" title="A página ainda não foi criada.">Segurança de aplicações web Django</a></li>
 <li><a href="/pt-BR/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
</ul>