Este conteúdo aborda testes automatizados, diferenciação entre testes unitários e integrados, e o uso de test runners como unittest para garantir qualidade de software com verificação repetível.
Testes automatizados verificam o comportamento de um software com entradas conhecidas, gerando saídas esperadas. Eles reduzem custos de verificação repetitiva e ajudam a detectar bugs antes da entrega.
Testes exploratórios e automatizados diferem principalmente em planejamento: exploratórios são sem roteiro; automatizados envolvem casos bem definidos para cobrir funcionalidades e entradas diversas.
Testes unitários validam uma função ou módulo isoladamente, assegurando que uma unidade específica se comporte como esperado.
def soma_elementos(lista):
total = 0
for n in lista:
total += n
return total
assert soma_elementos([1, 2, 3]) == 6
Testes integrados verificam a interação entre várias funções/módulos, assegurando que o conjunto funciona como um todo, mesmo que cada parte seja testada separadamente.
def calcular_total_aprox(lista):
parcial = soma_elementos(lista)
multiplicacao = multiplica(lista)
return parcial + multiplicacao
Para usar um test runner integrado, agrupe testes em uma classe que herda de TestCase e utilize asserts para comparar resultados esperados.
import unittest
def soma(lista):
total = 1 # intencionalmente incorreto no exemplo para detector de erro
for x in lista:
total += x
return total
def mult(lista):
total = 1
for x in lista:
total *= x
return total
class TestOperacoes(unittest.TestCase):
def test_soma(self):
self.assertEqual(soma([1,2,3]), 6)
def test_mult(self):
self.assertEqual(mult([1,2,3]), 6)
if __name__ == '__main__':
unittest.main()
Observação: o objetivo é mostrar como o teste unitário verifica uma função específica; em cenários reais, a função soma deveria iniciar com 0 para retornar 6 com [1,2,3].
Test runners executam uma suíte de testes, verificam saídas e geram relatórios, facilitando depuração. Exemplos: unittest (padrão Python), pytest, nose.
Para usar unittest, pode-se executar diretamente o arquivo de testes ou utilizar discovery para encontrar todos os testes que obedecem ao padrão test_*.py:
# Executar somente um arquivo
python test_operacoes.py
# Descobrir e executar todos os testes no diretório
python -m unittest discover -s . -p "test_*.py"
- Escreva testes repetíveis, com entradas bem definidas e sem dependências externas imprevisíveis.
- Use asserts descritivos e nomes de testes que expressem o comportamento verificado.
- Separe testes unitários de integrados para facilitar diagnóstico.
Resposta correta: B) Testes unitários focam em partes isoladas de código.
Explicação: Unitários validam unidades específicas (funções/métodos) isoladamente para verificar comportamento esperado.
Resposta correta: C) Uma aplicação que executa, gerencia e relata testes.
Explicação: Test runners coordenam a execução de casos de teste, agregam resultados e ajudam na depuração.
Resposta correta: B) Localizar automaticamente testes com padrões de nomenclatura.
Explicação: Discovery facilita a execução de grandes suítes sem listar manualmente cada arquivo de teste.
Resposta correta: B) Os demais testes continuam e são reportados ao final.
Explicação: Em frameworks modernos, a suíte executa todos os testes e agrega resultados, permitindo diagnóstico completo.
Bom pessoal, bem-vindos novamente a nossa disciplina de algoritmos e programação de computadores 2.
Estamos chegando no final da nossa disciplina e nesta última semana a gente vai ver mais dois assuntos que são interessantes para a formação de vocês.
O primeiro deles que a gente vai ver na aula de hoje é sobre testes automatizados que envolvem alguns conceitos para facilitar a verificação de corretude de programas que são desenvolvidos de modo a verificar o que acontece com esse programa que você está desenvolvendo se ele realiza o que ele proponha fazer.
Se existem problemas, se existem bugs ou excessões de entrada que poderiam gerar saídas e desperadas.
Então vamos ver como podemos implementar os chamados testes automatizados.
É muito importante que a gente realiza testes para determinar a qualidade de um software.
Ao longo desse curso que a gente fez, a gente já realizou vários testes que são chamados de testes exploratórios que são feitos para verificar se o código está funcionando e só que são testes que são feitos sem qualquer planejamento.
Então a gente chama isso de testes exploratórios.
Quando a gente tem um planejamento de um processo completo de verificação de software, isso envolve ele estar a todas as funcionalidades desse software e encontrar diferentes tipos de entrada de modo a gerar os resultados que vão verificar se os resultados gerados são aqueles que foram esperados.
Então por exemplo, se você tem um programa que executa a potência de um número, é importante verificar se para diferentes valores de base e de expoente, se o seu programa vai gerar o resultado correto, inclusive procurando por números negativos, zerados entre outros, de modo a verificar se o programa realmente lida com todas essas exceções com todos esses tipos de diferentes de entrada.
E a cada mudança de software vai ser necessário executar todo esse processo de verificação novamente, o que pode fazer com que os testes, quando realizados manualmente, se tornam caros e propensos aeiros.
Quando a gente automatiza esse processo de verificação de software, a gente vai formular, então, diferentes testes, que são chamados de testes unitários ou testes integrados, e aí dependendo do que a gente quer verificar, a gente vai escolher um ou outro tipo de testes.
No caso dos testes unitários, eles consistem apenas em verificar se uma determinada funcionalidade bem específica do teu software, se está executando com sucesso aquilo que ele se propõe a fazer.
Já os testes integrados, imagine a situação que você tem um programa que realiza um cálculo complexo com várias chamadas a diferentes funções.
Então, o que esse teste vai fazer? Ele vai verificar o software como um todo.
Então, você tem uma entrada que os oro vai dar e o programa vai gerar uma saída, e aí o teste vai fazer análise em relação a entrada, se aquela saída foi gerada como o esperado.
Só que, no caso de testes integrados, acaba sendo um desafio em identificar o problema, já que esse problema pode ter sido gerado por alguma função específica lá de todo o conjunto de funções que vão ser chamadas durante a execução daquele único testes.
Aqui, a gente pode ver um exemplo bastante simples de como a gente pode implementar uma verificação bem rápida de uma determinada função.
Então, a gente pode usar, por exemplo, o comando acerte, que vai verificar se uma determinada expressão que está aqui, se ela é verdadeira ou não.
Então, nesse caso, estou verificando se a função SAM, que é nativa do Python, se o resultado dela aplicado com uma lista com os elementos 1, 2 e 3, se a soma desses elementos vai dar 6.
Então, se for verdadeiro isso, quer dizer que esse teste ocorreu com sucesso que a função ela está corretta.
Já, por exemplo, nesse caso, aqui, de baixo, a gente chama novamente a mesma função e, lógico, que aqui o resultado vai dar false, porque 1 mais 1 não vai dar 6.
Então, o que vai ocorrer vai ser girada uma mensagem de erro que está aqui embaixo, e essa mensagem de erro é exatamente essa estranha que a gente coloca aqui após a viúla.
Então, o resultado da função acerte, do comando acerte, vai te dar uma mensagem de erro, e é isso que faz com que a gente tenha esse test implementado por meio da função acerte.
Para utilizar, então, isso, numa maneira mais estruturada, a gente vai ver um exemplo simples, que é testar duas funções, a primeira realiza a soma dos elementos de uma lista, e a outra realiza a multiplicação dos elementos de uma lista.
E, olhando esse código que está aí, nesses dois quadros, a gente consegue constatar que a função soma, ela está errada, ela foi implementada de uma maneira incorreta, pois, aqui, esse total foi inicializado com 1.
Então, quando a gente faz a soma dos elementos agregando aqui, nesta variável total, isso vai te dar um valor cujo resultado não é o verdadeiro, e já a função de multiplicar, ela realiza, basicamente, a mesma lógica, só que, aqui, você faz uma multiplicação e agrega nesta variável total, e, nesse caso, ela está correta, porque é a multiplicação, a variável total, começa com 1.
Então, suponha que essas duas funções estão definidas no norquivo.
pipson, e a gente vai, agora, estruturar um conjunto de testes para fazer a verificação dessas duas funções.
Então, a gente vai criar, além desse arquivo, fonte.
pipson, a gente vai ter mais um outro arquivo, que a gente vai chamar de test.
pipson, e, nesse arquivo, a gente vai ter esse código, que é a importação das duas funções que vão ser verificadas, soma e mult, e a gente vai definir, dentro desse arquivo, duas funções de testes.
Primeira é para verificar se a função soma, ver da DE, está correta, e a segunda é para verificar a função mult.
E, dentro de cada uma dessas funções, a gente coloca o comando a cert para fazer exatamente a verificação dessas, das chamadas, dessas duas funções soma e mult.
Bom, então, aqui eu já tenho o arquivo.
pipson, implementado com as duas funções, reparem que a função soma está incorreta, propositadamente.
E, aqui, eu tenho um arquivo test, que contém a importação das duas funções, e as duas funções aqui para fazer a verificação são os dois testes bonitários para verificar a função soma e verificar a função mult.
Então, quando a gente executa esse arquivo, com o nosso terminal, eu vou rodar Python test.
pipson, então, reparem que ele parou exatamente na função test soma, que é exatamente essa de cima.
Então, quando a gente chama aqui o test soma, ele vai executar esta função e vai executar o comando a cert.
E, nesse caso, ele já vai dar um resultado, uma mensagem de erro, porque a função soma está sendo chamada a partir desta definição.
E, como eu falei, a gente inicializou o total como um, então, um resultado desta função soma não vai te dar 6, mas sim o valor 7, que está incorreto, não é o esperado conforme a gente pode ver.
Com forma, a gente pode ver aqui, nesta encódio.
Então, pessoal, o que acontece aqui? Quando eu dá problema na primeira teste unitário, reparem que o segundo não é mais executado.
Então, quando dá um problema no algum teste unitário, os testes unitários que estão a seguir não vão ser executados.
Então, reparem que aqui o resultado da execução, ele simplesmente retorou a mensagem de erro, referente a chamada test soma, e a teste mult não foi executado.
Então, com isso, a gente tem um certo problema, porque supongo que você quer executar todos os casos de testes de uma vez, e depois fazer análise de todos os resultados, de todos os casos de testes após execução de todos eles.
Então, como que a gente pode fazer para executar todos os casos de testes mesmo que algum fale? Então, para isso, a gente faz uso dos chamados test runners, que são aplicações especialmente projetadas para executar testes, checar saída e fornecer ferramentas para depurar e também diagnosticar os testes e os programas.
Então, um test runner bastante conhecido é o unit test, que é um exemplo de biblioteca de test runner, que está disponível na biblioteca padrão Python.
Existem vários outros test runners, vocês podem depois pesquisar sobre eles, como o Nose, o Python test, entre vários outros que existem na literatura.
Para a gente usar o unit test, a gente tem que primeiro agrupar os testes em métodos de uma ou mais classes que vão ver dar de uma classe base, uma classe par chamada de test case, que está aqui.
Além disso, a gente precisa utilizar métodos específicos dessa classe, por exemplo, o acerte igual para cada caso de testes.
Então, vamos ver um exemplo.
Supondo que eu tenho o arquivo test.
fum.
pipsle, aqui estou definindo uma classe chamada de test sum, que é erda de test case, e ali dentro eu tenho duas funções chamadas de test sum 1 e test sum 2, que dentro de cada uma chama chamada, a função acerte equal, pode verificar se o resultado de uma função, por exemplo, a soma, é igual ao segundo parâmetro, que, nesse caso, é o valor 6, e caso não for igual, ele vai exibir a mensagem de erro lá na tela.
Fazendo a execução desse programa, aqui temos o test fum.
pips, com esse código, que eu acabei de explicar.
Agora, se a gente executa este arquivo, ao invés do outro, então a gente vai chamar que o Python, test fum.
pipsle, reparem que, agora, ele executou dois testes, como forma está aqui, sendo que ele mostra qual é o test que teve falha, que teve problema, que é da função test sum, que realiza exatamente a chamada a função suma que está em correto.
Mas, diferente do exemplo anterior, ele executou todos os casos de testes de uma vez, e depois ele mostrou o relatório, falando qual foi o test que deu problema.
A validação da saída em relação ao resultado esperado é o que a gente chama de Acercham.
E, como boas práticas, a gente tem que é importante a gente certificar que o test pode ser repetido várias vezes, e que a executar várias vezes este teste, o mesmo resultado seja gerado.
E, também, é importante a gente criar Acercham, especificas, que vão relacionar de fato a saída gerada com a entrada fornecida, então, no caso do Acerch igual, isso é apenas um exemplo, que é para verificar se um determinado valor é igual a outro valor.
A gente tem, por exemplo, Acerch True para verificar se uma determinada variável é verdadeira, Acerch False para verificar se ela é falsa entre várias outras opções que podem ser escolhidas para fazer essa verificação.
E, outra coisa que é importante também é que a gente pode executar, se a gente tiver vários arquivos de teste, é possível executar todos eles de uma vez a partir de um único comando.
E isso é feito por meio da criação de arquivos de testes que começem com o prefixo test.
Então, se você tiver o prefixo teste, quando a gente executa este comando que está aqui, ele vai procurar por todos os arquivos que começam com testes e vai executar todos eles de uma vez.
Então, nesse caso, aqui eu tenho apenas um arquivo que é o test fundo.
tipson, então, tem o prefixo teste, e a gente pode executar aquele comando com Python menos m e unite testes, que vai procurar por todos os arquivos que começam com testes e vai executar cada um deles separadamente, mas todos de uma vez.
To legal? Também é possível a gente executar esses casos de teste dentro de uma IDE.
No caso do Python Charm, a gente tem também uma funcionalidade bastante intuitiva de executar os casos de teste.
Então, aqui na nossa IDE, eu posso simplesmente clicar com o botão direito na pasta do projeto, e aqui a gente tem a opção rune-unitest.
Então, quando a gente executa esta opção, ele vai executar todos os casos de teste e vai gerar para a gente aqui o relatório associado a todos os casos de testes que foram executados ali naquele programa, que provém também dos arquivos que tem o prefixo teste lá no nosso projeto.
Bom, essa foi a nossa videoaula sobre testes automatizados.
Esta videoaula teve o intuito simplesmente de mostrar para vocês que existe essa funcionalidade, tanto na linguagem parte, ou como em várias outras, que são os testes automatizados.
Existem vários outros conceitos relacionados a este assunto, mas a gente só viu o básico aqui nessa disciplina, e espero que vocês possam ter compreendido.
E a gente se vê então na nossa próxima videoaula sobre um outro assunto, também que é bastante interessante para a nossa disciplina.
Obrigado e até lá!