Resumo : neste tutorial, você aprenderá sobre o conceito de teste de unidade e como usar o unittest
módulo Python para realizar testes de unidade.
O que é um teste de unidade
Um teste de unidade é um teste automatizado que:
- Verifica um pequeno pedaço de código chamado unidade. Uma unidade pode ser uma função ou um método de uma classe .
- Corre muito rápido.
- Executa de forma isolada.
A ideia do teste unitário é verificar cada pequena parte do seu programa para garantir que ele funcione corretamente. É diferente do teste de integração, que testa se diferentes partes do programa funcionam bem juntas.
O objetivo de um teste unitário é encontrar bugs. Além disso, um teste de unidade pode ajudar a refatorar o código existente para torná-lo mais testável e robusto.
Python fornece um módulo integrado unittest
que permite realizar testes de unidade de maneira eficaz.
Terminologia xUnit
O unittest
módulo segue a filosofia xUnit. Possui os seguintes componentes principais:
- Sistema em teste é uma função, uma classe, um método que será testado.
- Classe de caso de teste (
unittest.TestCase
): é a classe base para todas as classes de teste. Em outras palavras, todas as classes de teste são subclasses da classe TestCase nounittest
módulo. - Acessórios de teste são métodos executados antes e depois da execução de um método de teste.
- Asserções são métodos que verificam o comportamento do componente que está sendo testado.
- Conjunto de testes é um grupo de testes relacionados executados em conjunto.
- Test runner é um programa que executa o conjunto de testes.
Exemplo de teste unitário do Python
Suponha que você tenha Square
uma classe que possui uma propriedade chamada length
e um método area()
que retorna a área do quadrado. A Square
aula está no square.py
módulo:
class Square:
def __init__(self, length) -> None:
self.length = length
def area(self):
return self.length * self.length
Linguagem de código: Python ( python )
Para testar a Square
classe, você cria um novo arquivo chamado test_square.py
file e importa o unittest
módulo assim:
import unittest
Linguagem de código: Python ( python )
Como é test_square.py
necessário acessar a Square
classe, você deve importá-la do square.py
módulo:
import unittest
from square import Square
Linguagem de código: Python ( python )
Para criar um caso de teste, você define uma nova classe chamada TestSquare
que herda da TestCase
classe do unittest
módulo:
class TestSquare(unittest.TestCase):
pass
Linguagem de código: Python ( python )
Para testar o area()
método, você adiciona um método chamado test_area()
à TestSquare
classe assim:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
Linguagem de código: Python ( python )
No test_area()
método:
- Primeiro, crie uma nova instância da
Square
classe e inicialize seu raio com o número 10. - Segundo, chame o
area()
método que retorna a área do quadrado. - Terceiro, chame o
assertEqual()
método para verificar se o resultado retornado peloarea()
método é igual a uma área esperada (100
).
Se a área for igual a 100, assertEqual()
passará no teste. Caso contrário, o assertEqual()
teste falhará.
Antes de executar o teste, você precisa chamar a main()
função do unittest
módulo da seguinte forma:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
if __name__ == '__main__':
unittest.main()
Linguagem de código: Python ( python )
Para executar o teste, abra o terminal, navegue até a pasta e execute o seguinte comando:
python test_square.py
Linguagem de código: Python ( python )
Se você usa Linux ou macOS, você precisa usar o comando python3:
python3 test_square.py
Linguagem de código: Python ( python )
Ele produzirá o seguinte:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Linguagem de código: texto simples ( texto simples )
A saída indica que um teste foi aprovado, indicado pelo ponto (.). Se um teste falhar, você verá a letra F
em vez do ponto (.)
Para obter informações mais detalhadas sobre o resultado do teste, você passa o verbosity
argumento com valor 2 para a unittest.main()
função:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
if __name__ == '__main__':
unittest.main(verbosity=2)
Linguagem de código: Python ( python )
Se você executar o teste novamente:
python test_square.py
Linguagem de código: Python ( python )
você obterá as informações detalhadas:
test_area (__main__.TestSquare) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Linguagem de código: texto simples ( texto simples )
A saída lista o caso de teste com o resultado ok desta vez em vez do ponto (.)
Executando testes sem chamar a função unittest.main()
Primeiro, remova o if
bloco que chama a unittest.main()
função:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
Linguagem de código: Python ( python )
Segundo, execute o seguinte comando para executar o teste:
python3 -m unittest
Linguagem de código: Python ( python )
Este comando descobre todas as classes de teste cujos nomes começam com Test*
localizados no test_*
arquivo e executa os métodos de teste que começam com test*
. a -m
opção representa o módulo.
Neste exemplo, o comando executa o test_area()
método da TestSquare
classe no test_square.py
módulo de teste.
Se você usa macOS ou Linux, você precisa usar o python3
comando:
python3 -m unittest
Linguagem de código: Python ( python )
Ele retornará algo como:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Linguagem de código: Python ( python )
Para exibir mais informações, você pode adicionar -v
opções ao comando acima. v
significa verbosidade. É como chamar o unittest. main()
com argumento de verbosidade com valor 2.
python -m unittest -v
Linguagem de código: Python ( python )
Saída:
test_area (test_square.TestSquare) ... ok
----------------------------------------------------------------------
Ran 1 tests in 0.000s
OK
Linguagem de código: Python ( python )
Testando exceções esperadas
O Square
construtor aceita um length
parâmetro. O length
parâmetro deve ser um int
ou float
. Se você passar o valor que não está nesses tipos, o Square
construtor deverá gerar uma TypeError
exceção.
Para testar se o Square
construtor gera a TypeError
exceção, você usa o assertRaises()
método em um gerenciador de contexto como este:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
def test_length_with_wrong_type(self):
with self.assertRaises(TypeError):
square = Square('10')
Linguagem de código: Python ( python )
Se você executar o teste novamente, ele falhará:
python -m unittest -v
Linguagem de código: Python ( python )
Saída:
test_area (test_square.TestSquare) ... ok
test_length_with_wrong_type (test_square.TestSquare) ... FAIL
======================================================================
FAIL: test_length_with_wrong_type (test_square.TestSquare)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python-unit-testing\test_square.py", line 13, in test_length_with_wrong_type
with self.assertRaises(TypeError):
AssertionError: TypeError not raised
----------------------------------------------------------------------
Ran 2 tests in 0.001s
Linguagem de código: texto simples ( texto simples )
O test_length_with_wrong_type()
método esperava que o Square
construtor gerasse uma TypeError
exceção. No entanto, isso não aconteceu.
Para passar no teste, você precisa gerar uma exceção se o tipo da length
propriedade não estiver int
ou estiver float
no Square
construtor:
class Square:
def __init__(self, length) -> None:
if type(length) not in [int, float]:
raise TypeError('Length must be an integer or float')
self.length = length
def area(self):
return self.length * self.length
Linguagem de código: Python ( python )
Agora, todos os testes passam:
python -m unittest -v
Linguagem de código: Python ( python )
Saída:
test_area (test_square.TestSquare) ... ok
test_length_with_wrong_type (test_square.TestSquare) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Linguagem de código: Python ( python )
O exemplo a seguir adiciona um teste que espera uma ValueError
exceção se length
for zero ou negativo:
import unittest
from square import Square
class TestSquare(unittest.TestCase):
def test_area(self):
square = Square(10)
area = square.area()
self.assertEqual(area, 100)
def test_length_with_wrong_type(self):
with self.assertRaises(TypeError):
square = Square('10')
def test_length_with_zero_or_negative(self):
with self.assertRaises(ValueError):
square = Square(0)
square = Square(-1)
Linguagem de código: Python ( python )
Se você executar o teste, ele falhará:
python -m unittest -v
Linguagem de código: Python ( python )
Saída:
test_area (test_square.TestSquare) ... ok
test_length_with_wrong_type (test_square.TestSquare) ... ok
test_length_with_zero_or_negative (test_square.TestSquare) ... FAIL
======================================================================
FAIL: test_length_with_zero_or_negative (test_square.TestSquare)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python-unit-testing\test_square.py", line 17, in test_length_with_zero_or_negative
with self.assertRaises(ValueError):
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
Linguagem de código: Python ( python )
Para fazer o teste passar, você adiciona outra verificação ao Square()
construtor:
class Square:
def __init__(self, length) -> None:
if type(length) not in [int, float]:
raise TypeError('Length must be an integer or float')
if length < 0:
raise ValueError('Length must not be negative')
self.length = length
def area(self):
return self.length * self.length
Linguagem de código: Python ( python )
Agora, todos os três testes passam:
python -m unittest -v
Linguagem de código: Python ( python )
Saída:
test_area (test_square.TestSquare) ... ok
test_length_with_wrong_type (test_square.TestSquare) ... ok
test_length_with_zero_or_negative (test_square.TestSquare) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
Linguagem de código: texto simples ( texto simples )
Resumo
- Um teste de unidade é um teste automatizado que verifica um pequeno trecho de código, é executado rapidamente e de maneira isolada.
- Use o
unittest
módulo para realizar testes de unidade. - Crie uma classe que herda da
unittest.TestCase
classe para fazer um caso de teste. - Use o
assertEqual()
método para testar se dois valores são iguais. - Use o
assertRaises()
método em um gerenciador de contexto para testar exceções esperadas. - Use o
python -m unittest -v
comando para executar um teste.