[MÚSICA] Então vamos lá. Nós já vimos como usar o arcabouço de testes automatizados pytest para escrever testes para os nossos programas Python, para ter uma certa garantia de que a coisa está funcionando. Mas vamos agora ver novas ferramentas dentro do pytest, novos mecanismos e, também, essa questão do que é código testável. Às vezes, o código que a gente escreve, não necessariamente, ele foi escrito de uma forma que permite ser testado. Particular, código que imprime muitas coisas na tela vez de devolver valores é mais difícil de testar porque o código do teste não consegue ver aquilo que foi impresso. Então, quando a gente escreve software a gente deve pensar produzir código testável, código de alta qualidade é código testável. Não interessa se o seu código está muito bem feito e ele não pode ser testado, aí ele não é bom código, não é código de alta qualidade. Então, alguns casos pode ser necessário refatorar o código para que ele se torne mais testável. Vamos ver exemplo disso? Quando eu implementei aqui o Baskhara para calcular as raízes de uma equação do segundo grau, eu fiz essa implementação aqui junto com vocês, se vocês quiserem relembrar vocês podem ir no vídeo que eu expliquei isso. E note que, nessa implementação ele simplesmente imprime várias coisas sobre a raíz, então, fica difícil a gente testar se ele está calculando a coisa certa. Então, como a gente poderia refatorar o código, ou seja reorganizar de forma que seja mais fácil de testar ele. Eu vou reestruturar esse código de forma a que que a função aqui imprime_raizes, vez de ficar imprimindo na tela, ela devolva informações sobre quasi são as raízes. Então, a primeira coisa, já vamos mudar o nome dela. Vez de se chamar imprime_raizes, ela vai se chamar calcula_raizes. Daí que ele calcula o delta, se delta igual a zero, ele calcula uma única raiz e daí imprime "A única raiz é" tal. Eu vou tirar esse print, então vez de falar que a única raiz é tal, eu vou devolver a raiz. Então, eu vou fazer aqui return raiz1. Agora, uma coisa importante: a equação de segundo grau às vezes ela tem zero raízes reais, às vezes tem uma, às vezes tem duas. Então, a gente precisa dizer para quem está nos chamando essa informação de quantas raízes tem. Então, eu vou fazer o seguinte: como Python a gente pode devolver ou mais valores num return, eu vou fazer de forma que o primeiro valor é o número de raízes e depois, eu devolvo as raízes. Então, aqui eu vou devolver return1, e raiz1 para indicar que tem uma única raiz e é raiz1. Aqui, nesse caso, que não possui raízes reais, eu vou devolver zero para indicar que tem zero raízes, return zero. E aqui eu preciso devolver as duas raízes. Então, eu vou fazer return dois para dizer que tem duas raízes e daí a raiz1 e a raiz2, pronto. E agora, para que isso aqui continue funcionando, eu vou ter que mudar o main, porque o main agora vez de chamar imprime_raizes ele vai chamar o calcula_raizes e, o calcula_raizes vai devolver essa sequência de números correspondentes ao primeiro número de raízes e depois as raízes. Então, nesse caso, eu podia fazer if para dependente do número de raízes imprimir uma mensagem ou outra, só para simplificar vou dar print que simplesmente vai imprimir a sequência, a lista de valores que o calcula_raizes devolve. Isso aqui vai imprimir. Então, deixa eu vou salvar isso aqui, vou executar. Daí eu posso executar o main ou o calcula_raizes. Se eu executar o main aqui ele vai pedir o valor de a, vamos supor que eu vou chamar aqui dez, dez, dez e nesse caso tem zero raízes. Por outro lado, se for dez, vinte, dez, aí tem uma raiz só e a raiz é menos. Esse é caso, se não me engano, que o delta deu zero, por isso que tem uma raiz. Então, está bom, o nosso programa está funcionando e agora é muito mais fácil testar porque eu posso chamar esse calcula_raizes com diferentes valores e vou testando esses diferentes valores. Então, para fazer isso, eu vou criar outro arquivo aqui, vou criar novo arquivo e nesse novo arquivo eu vou fazer o import daquele arquivo do Baskhara, o arquivo se chama Bhaskara, com B maiúsculo, Bhaskara, estou a fazer esse import do Bhaskara e daí eu vou criar uma classe de teste aqui. Então, a minha classe vai se chamar TestBhaskara. Então, tem que começar o nome da classe com Test, com T maiúsculo, para o pytest identificar que isso é uma classe de teste e aqui dentro eu vou ter as minhas várias funções de teste. Então, eu vou, por exemplo, testa_uma_raiz qual é o método, tem que ter esse teste aqui e eu vou testar aqui, vejamos. Tem outra mudança que eu quero fazer aqui. Eu quero tornar esse código orientado a objetos, está me incomodando o fato de isso aqui ser funções jogadas aí, e não dentro de uma classe. Então, vamos colocar isso aí dentro de uma classe que o código vai ficar muito mais organizado, estava muito feio o código com essas funções jogadas ali. Então, vamos criar uma classe chamada Bhaskara [SEM ÁUDIO] e daí dentro dessa classe a gente vai ter aqui, será que eu consigo, acho que o editor aqui do IDLE permite indentar tudo de uma vez. Vejamos aqui Format, Indent region. É, ele indentou tudo de uma vez. Acho que vai dar certo, vamos ver. Então aqui, essas funções deixam de ser funções, passam a ser métodos da classe, então eu preciso introduzir o self aqui, agora quando eu chamo aqui o delta eu vou fazer self.delta, quando eu chamo aqui o calcula_raizes eu vou fazer o self.calcula_raizes. Pronto, será que falta mudar mais alguma coisa? Acho que não, acho que está tudo certo. Então, já temos a nossa classe Bhaskara, deixa eu só testar aqui, nós vamos executar uma vez. Compilou, daí o que eu vou ter que criar a minha classe Bhaskara. [SEM ÁUDIO] B = Bhaskara. Está, deu certo e agora b.main, dez, vinte, dez, deu certo. Então, pronto. Já convertemos, nós temos agora código orientado a objetos, então aqui na testa_uma_raiz eu vou testar uma possibilidade que tenha só uma raiz. Eu vou voltar aqui, se a gente olhar como testar Bhaskara? Eu vou querer testar diferentes casos, particular, tem aqueles casos de zero raizes, uma raiz e duas raízes, eu vou querer testar no mínimo esses três casos diferentes. Então, pensando, essa equação x quadrado é igual a zero ela tem uma única raiz. Então, vamos fazer isso, testa com uma raiz, então x quadrado igual a zero significa que eu vou ter que chamar com a valendo e b e c valendo zero. Então, vai ser zero, zero. Mas primeiro, para testar eu preciso criar uma instância da classe Bhaskara. Então, tenho que fazer isso aqui: b recebe Bhaskara e daí sim, eu posso fazer assert, b ponto calcula raizes e daí com a, b, c, vai ser zero, zero. E daí, isso a gente viu que vai ser uma única raiz e, nesse caso, vocês podem ver aqui a raiz aqui x é igual a zero. Então, vai ser zero a raiz. Então, isso aqui eu acho que está dando certo. Vamos testar esse caso aqui. Para testar, vou salvar isso aqui como test_bhaskara e eu vou salvar nesse diretório pytest aqui. Não, eu vou salvar no mesmo diretório que eu estava antes porque o Bhaskara estava lá. Está, test_bhaskara. Então, vamos aqui. Vamos ver se o Bhaskara está aqui? É, o test_bhaskara está aí. Então, eu posso fazer: pytest test_bhaskara. E, deu erro de sintaxe aqui, invalid syntax na classe TesteBhaskara, eu já sei, eu esqueci de dois pontos. Novamente, pronto. Pelo menos avançou mas ainda deu algo errado. Ele falou que ele não conseguiu instanciar esse Bhaskara aqui, porque esse Bhaskara é dentro do módulo Bhaskara. Então, a gente, precisamos colocar o nome do módulo aqui, vai ser Bhaskara.Bhaskara, o nome do módulo e depois o nome da classe. Vamos ver se deu certo agora. Pronto, agora deu certo, passou o teste aqui. Então, testamos com uma raiz, agora vamos ver o próximo caso. O próximo caso seria, por exemplo, duas raízes e eu coloquei exemplinho. Eu coloquei x menos três vezes x menos dois, que dá isso aqui: x ao quadrado menos cinco x igual a zero. Isso vai ter duas raízes e a raiz vai ser três e dois, que são os lugares que essa equação aqui encontra o zero. Então, vai ser três e dois. Então, vamos testar esse caso. Eu vou ter que passar como parâmetros de a, b, c, menos cinco e seis. Então, a gente copia isso aqui, que vai dar aqui menos cinco e seis. Daí a resposta tem que ser, que ele vai ter duas raízes e as raízes vão ser três e dois. É isso? Três e dois, isso. Então, está certo. Agora que outro caso nós queremos testar? Nós queremos testar com zero raízes reais. Esse aqui é exemplo que eu já tinha mostrado, dez x quadrado mais dez x mais dez, isso aqui não tem nenhuma raiz, delta dando negativo não nenhuma raiz real. Então dez, dez, dez. Desculpa, tenho que mudar o nome aqui desse teste. testa_duas_raizes e eu vou ter terceiro teste que eu vou chamar de testa_zero_raizes, reais. E daí vai ser com dez, dez, dez, dez, dez, e dá zero, tem que dar zero raízes. Está. Eu poderia ter outro caso, onde eu quero testar, caso que dê uma raiz negativa. [SEM ÁUDIO] Raiz negativa. Foi esse caso que a gente viu aqui, lembra que a gente fez esse aqui, dez, vinte, dez, dá uma raiz negativa. Vamos testar isso aqui também. Então, dez, vinte, dez, tem que dar que ele tem uma raiz e, essa raiz é menos. Então, pronto já temos aqui uma bateria de testes para o nosso Bhaskara. Vamos executar e ver se está funcionando. Quatro testes se passaram, então, o meu Bhaskara aqui parece que está muito bem comportado, pelo menos nesses quatro testes que pegou quatro casos diferentes, a coisa funcionou e os nossos testes já estão organizados dentro de uma classe, TestBhaskara, e estão testando uma outra classe, que é essa classe Bhaskara aqui, que foi uma classe que foi refatorada de forma a se tornar mais orientada a objetos e mais passível de teste. Muitas vezes a gente vai ter que fazer isso, refatorar o nosso código para que ele fique mais testável. Vamos continuar mais pouco aqui? A gente já viu esse caso aqui. E aprender a simplificar o código, que são as fixtures. Se você perceber bem o nosso código aqui, tem uma coisa que está incomodando. Lembra que código repetido é sempre uma má ideia e aqui está repetido b = Bhaskara.Bhaskara todas as vezes. A gente está tendo que instanciar novo objeto todas as vezes. E tem uma coisa que se chama fixture que ajuda exatamente nesses casos. Então, uma fixture é valor fixo para conjunto de testes, normalmente o conjunto de testes compartilha essa fixture e ele é usado muitas vezes para guardar objeto que requer pouco mais de trabalho para ser criado. Para definir uma fixture usando pytest Python, a gente implementa uma função que cria o objeto que a gente quer que seja fixture e, marcamos essa função com @pytest.fixture. Então, vamos ver como que a gente poderia reorganizar este teste para que esse b aqui seja uma fixture. É muito simples, basta a gente escrever aqui @pytest.fixture, para a gente dizer que a gente está definindo uma fixture. E daí, a gente dá nome para essa fixture, eu vou chamar de b e vai ser uma função. Nesse caso é método da classe TestBhaskara. O que é que esse método vai devolver? Ele vai devolver exatamente o que a gente queria que fosse guardado no b, que é esse Bhaskara.Bhaskara. Aqui, pronto. E daí, a partir daquele momento, todos os métodos que a gente quiser usar aquela fixture a gente passa como parâmetro esse b. Então, esse b aqui tem que ter exatamente o mesmo nome desse b, que vai ser da fixture, e daí, todo o mundo vai receber isso aqui, todos os métodos vão receber isso como parâmetro, mas não preciso mais dessa linha. Então, essa linha que estava repetida quatro vezes, vai sumir. Vírgula b, aqui some a linha, [SEM ÁUDIO] e aqui vírgula b e também some a linha. Então, essa aqui foi uma refatoração que o código continua fazendo exatamente a mesma coisa, vamos executar novamente. Eu errei alguma coisa. Para a gente poder usar a fixture, a gente tem que importar o módulo pytest, então preciso: import pytest. Agora, a gente pode usar a fixture, pronto. Então, os quatro testes aqui passaram e agora o código ficou mais enxuto. Então, esse b aqui é uma fixture é alguma coisa que a gente pode usar, então algo comum a uma coleção de testes, nesse caso. É uma instância da classe Bhaskara. Então, fixtures são bastante úteis para isso. Tem uma outra coisa que é muito útil, que é essa ideia de parametrização. Quando a gente quer escrever determinado código e a gente quer testar ele vários casos mas, o teste é muito semelhante, daí a gente pode usar esse pytest.mark.parametrize. A gente usa isso aqui, para testar a mesma coisa, nesse caso é exemplo que eu estou testando o método cubo, que deveria elevar ao cubo determinado valor. E daí, eu tenho método de teste chamado test_cubo e eu quero que ele tenha aqui uma entrada e teste se o valor devolvido pelo método cubo ao chamar como parâmetro o valor da entrada, se ele devolve determinado valor esperado. E para isso eu dou aqui nesse parametrize uma lista de pares, entrada, valor esperado, entrada, valor esperado, entrada, valor esperado. E o formato é exatamente esse. No primeiro campo eu dou entre aspas aqui o nome das variáveis, que seria entrada e valor esperado aqui, variáveis que devem ser passadas para cá e depois, aqui uma lista desses conjuntos, dessas tuplas, duplas, ou triplas, etcetera, de valores que vão ser passados aqui para esse método. Então, zero ao cubo dá zero, ao cubo dá dois ao cubo dá oito, menos dois ao cubo dá menos oito, dez ao cubo dá mil, por isso, então, aqui são cinco casos de testes onde eu gostaria de fazer isso aqui no cubo. Vamos pensar, então, agora como que eu poderia reorganizar? Na aula passada eu fiz o fatorial, vamos ver se o fatorial está aqui. Ele está aqui, fatorial. Lembra? Eu fiz isso aqui, eu defini o fatorial e depois fiz monte de testes aqui do fatorial. Mas vez de fazer esse monte de testes, eu poderia ter usado o parametrize. Como que eu poderia fazer isso? Primeira coisa, eu preciso do import do pytest, que pode estar aqui ou lá cima, e daí, eu vou dizer que casos que eu quero testar ali o meu fatorial. Então, eu vou fazer @pytest.mark.parametrize Daí aqui eu dou o nome dos valores, eu vou chamar de entrada e o valor esperado. Daí aqui eu começo a minha lista e eu vou dizer os valores. Então, zero fatorial dá fatorial dá eu vou querer que o menos dez devolva zero. [TOSSE] Deixa eu ver que outros casos tem aqui. Eu vou querer que o quatro dê vinte e quatro, eu vou querer que o cinco dê cento e vinte. Eu fecho aqui, fecho aqui. E daí, vou ter único método de teste, e daí, esse método de teste eu vou chamar por exemplo, test_fatorial, pode ser português também, testa_fatorial. Começa com test, e daí, eu vou precisar dos dois valores à entrada, e o valor esperado, eu vou dizer que o fatorial aqui da entrada tem de ser igual ao valor esperado. Daí eu vou salvar aqui o meu fatorial. Vamos executar aqui. Estou no diretório pytest, e eu vou fazer o py.test, o nome do arquivo é o test_fatorial. Você vê aqui, cinco testes passaram, então, que são esses aqui, dois, três, quatro, cinco. Essa é uma forma mais enxuta da gente escrever uma sequência de testes parametrais. Então, eu quero deixar exercício para vocês. Escrevam uma versão do TestaBhascara, usando esse @pytest.mark.parametrize. Então, aqui o TestaBhaskara que a gente fez, esse aqui é o Bhaskara e esse aqui é o TestaBhaskara. Está vendo que está repetindo aqui, quatro vezes. Eu quero que tenha único método teste aqui, daí, você usa o parametrize para definir esses quatro valores de teste, aproveita para colocar mais uns outros valores de teste. Esse é o primeiro exercício, outro exercício é, pegue o seu código preferido que você escreveu até agora e escreva uma bateria de testes para ele. Deixo esses exercícios para você. Resumindo, uma boa bateria de testes explicita o comportamento do código. Você olhando para a bateria de testes você entende quais são os principais casos diferentes que aquele código tem que lhe dar, você entende o comportamento que o código deve ter. Além disso, ele protege o código de mudanças por terceiros, se você escreve conjunto de classes e tem os testes para aquele conjunto de classes, você entrega esse conjunto de classes para outras pessoas programarem, as pessoas vão continuar estendendo esse conjunto de classes, mas vão continuar executando a sua bateria de testes. Isso vai dar uma garantia para ele de que não estão quebrando o pedaço que você fez. Além disso, eles vão escrever testes novos para cobrir o pedaço do código novo que eles vão criar. Além disso, essa bateria de testes protege o código de nós mesmos, porque às vezes a gente demora muitos meses para desenvolver software. Então, se você escreve código, três meses depois, você não lembra nada do que você escreveu. Mas, se você tem aquela bateria de testes, a bateria de testes vai testar para garantir que o comportamento daquele código é o que você espera. E também, você lendo os testes, você entende qual é o comportamento esperado. Então, essa bateria de testes vai garantir que o código funciona para os casos importantes, É bom lembrar que ele não garante que funciona sempre. Porque você nunca vai conseguir uma bateria de testes que cobre todos os possíveis casos, porque programas complexos são bilhões, trilhões de casos, então, você não vai conseguir testar todos os casos, mas você tenta pegar os principais conjuntos de casos e garantir que para aqueles principais conjuntos a coisa está funcionando. Quanto mais robusta a sua bateria de testes, mais certeza, mais segurança você vai ter que o código está funcionando bem. Lembre, código sem testes é código de má qualidade, e a gente aqui, nosso objetivo é construir software de alta qualidade. A gente não está aqui para construir software ruim. A gente está aqui para construir software bom e, para isso, você precisa ter testes automatizados para o seu código. Testes automatizados são fundamentais, não se esqueça disso. [MÚSICA] [MÚSICA] [MÚSICA]