Teste de unidade Python – 03

Resumo : neste tutorial, você aprenderá sobre o conceito de teste de unidade e como usar o unittestmó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 unittestque permite realizar testes de unidade de maneira eficaz.

Terminologia xUnit

O unittestmó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 no unittestmó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 Squareuma classe que possui uma propriedade chamada lengthe um método area()que retorna a área do quadrado. A Squareaula está no square.pymódulo:

class Square:
    def __init__(self, length) -> None:
        self.length = length

    def area(self):
        return self.length * self.lengthLinguagem de código:  Python  ( python )

Para testar a Squareclasse, você cria um novo arquivo chamado test_square.pyfile e importa o unittestmódulo assim:

import unittestLinguagem de código:  Python  ( python )

Como é test_square.pynecessário acessar a Squareclasse, você deve importá-la do square.pymódulo:

import unittest

from square import SquareLinguagem de código:  Python  ( python )

Para criar um caso de teste, você define uma nova classe chamada TestSquareque herda da TestCaseclasse do unittestmódulo:

class TestSquare(unittest.TestCase):
   passLinguagem de código:  Python  ( python )

Para testar o area()método, você adiciona um método chamado test_area()à TestSquareclasse 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 Squareclasse 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 pelo area()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 unittestmó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.pyLinguagem de código:  Python  ( python )

Se você usa Linux ou macOS, você precisa usar o comando python3:

python3 test_square.pyLinguagem de código:  Python  ( python )

Ele produzirá o seguinte:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OKLinguagem 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 Fem vez do ponto (.)

Para obter informações mais detalhadas sobre o resultado do teste, você passa o verbosityargumento 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.pyLinguagem de código:  Python  ( python )

você obterá as informações detalhadas:

test_area (__main__.TestSquare) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OKLinguagem 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 ifbloco 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 unittestLinguagem 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 -mopção representa o módulo.

Neste exemplo, o comando executa o test_area()método da TestSquareclasse no test_square.pymódulo de teste.

Se você usa macOS ou Linux, você precisa usar o python3comando:

python3 -m unittestLinguagem de código:  Python  ( python )

Ele retornará algo como:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OKLinguagem de código:  Python  ( python )

Para exibir mais informações, você pode adicionar -vopções ao comando acima. vsignifica verbosidade. É como chamar o unittest. main()com argumento de verbosidade com valor 2.

python -m unittest -vLinguagem de código:  Python  ( python )

Saída:

test_area (test_square.TestSquare) ... ok
 

----------------------------------------------------------------------
Ran 1 tests in 0.000s

OKLinguagem de código:  Python  ( python )

Testando exceções esperadas

O Squareconstrutor aceita um lengthparâmetro. O lengthparâmetro deve ser um intou float. Se você passar o valor que não está nesses tipos, o Squareconstrutor deverá gerar uma TypeErrorexceção.

Para testar se o Squareconstrutor gera a TypeErrorexceçã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 -vLinguagem 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.001sLinguagem de código:  texto simples  ( texto simples )

O test_length_with_wrong_type()método esperava que o Squareconstrutor gerasse uma TypeErrorexceção. No entanto, isso não aconteceu.

Para passar no teste, você precisa gerar uma exceção se o tipo da lengthpropriedade não estiver intou estiver floatno Squareconstrutor:

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.lengthLinguagem de código:  Python  ( python )

Agora, todos os testes passam:

python -m unittest -vLinguagem 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

OKLinguagem de código:  Python  ( python )

O exemplo a seguir adiciona um teste que espera uma ValueErrorexceção se lengthfor 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 -vLinguagem 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.lengthLinguagem de código:  Python  ( python )

Agora, todos os três testes passam:

python -m unittest -vLinguagem 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

OKLinguagem 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 unittestmódulo para realizar testes de unidade.
  • Crie uma classe que herda da unittest.TestCaseclasse 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 -vcomando para executar um teste.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *