O teste de software é o processo no qual um desenvolvedor garante que a saída real do software corresponda à saída desejada, fornecendo algumas entradas de teste ao software. O teste de software é uma etapa importante porque, se executado corretamente, pode ajudar o desenvolvedor a encontrar bugs no software em muito menos tempo.

O teste de software pode ser dividido em duas classes, teste manual e teste automatizado . O teste automatizado é a execução de seus testes usando um script em vez de um humano. Neste artigo, discutiremos alguns dos métodos de teste automatizado de software com Python.

Vamos escrever um aplicativo simples sobre o qual realizaremos todos os testes.

class Square: 
    def __init__(self, side): 
      
        self.side = side 
  
    def area(self): 
      
        return self.side**2
  
    def perimeter(self): 
      
        return 4 * self.side 
  
    def __repr__(self): 
      
        s = 'Square with side = ' + str(self.side) + '\n' + \ 
        'Area = ' + str(self.area()) + '\n' + \ 
        'Perimeter = ' + str(self.perimeter()) 
        return s 
  
  
if __name__ == '__main__': 
    
    side = int(input('enter the side length to create a Square: ')) 
      
    
    square = Square(side) 
  
    
    print(square) 

Observação: para obter mais informações sobre a função __repr__(), consulte este artigo .

Agora que temos nosso software pronto, vamos dar uma olhada na estrutura de diretórios de nossa pasta de projeto e depois disso, vamos começar a testar nosso software.



---Teste de software
   | --- __init__.py (para inicializar o diretório como um pacote python)
   | --- app.py (nosso software)
   | --- testes (pasta para manter todos os arquivos de teste)
           | --- __init__.py

O módulo 'unittest'

Um dos principais problemas do teste manual é que ele requer tempo e esforço. No teste manual, testamos o aplicativo em alguma entrada; se ele falhar, anotamos ou depuramos o aplicativo para aquela entrada de teste específica e, em seguida, repetimos o processo. Com unittest, todas as entradas de teste podem ser fornecidas de uma vez e, em seguida, você pode testar seu aplicativo. No final, você obtém um relatório detalhado com todos os casos de teste com falha claramente especificados, se houver.

O unittestmódulo possui uma estrutura de teste integrada e um executor de teste. Uma estrutura de teste é um conjunto de regras que devem ser seguidas ao escrever casos de teste, enquanto um executor de teste é uma ferramenta que executa esses testes com um monte de configurações e coleta os resultados.

Instalação: unittest está disponível no PyPI e pode ser instalado com o seguinte comando -

pip install unittest

Uso: escrevemos os testes em um módulo Python (.py). Para executar nossos testes, simplesmente executamos o módulo de teste usando qualquer IDE ou terminal.

Agora, vamos escrever alguns testes para nosso pequeno software discutido acima usando o unittestmódulo.

  1. Crie um arquivo com o nome tests.pyna pasta “testes”.
  2. Em tests.pyimportação unittest.
  3. Crie uma classe chamada TestClassque herde da classe unittest.TestCase.

    Regra 1: Todos os testes são escritos como métodos de uma classe, que devem ser herdados da classe unittest.TestCase.

  4. Crie um método de teste conforme mostrado abaixo.
    Regra 2: o nome de cada método de teste deve começar com “teste”, caso contrário, será ignorado pelo executor de teste.
    filter_none

    editar
    fechar

    play_arrow

    link
    brilho_4
    código

    def test_area(self):
        
          
        sq = Square(2)   
     
        
        
     
        self.assertEqual(sq.area(), 4
            f'Area is shown {sq.area()} for side = {sq.side} units')
    chevron_right
    
    
    filter_none
    
    

    Regra 3: usamos assertEqual()instruções especiais em vez das instruções embutidas assertdisponíveis no Python.

    O primeiro argumento de assertEqual()é a saída real, o segundo argumento é a saída desejada e o terceiro argumento é a mensagem de erro que seria exibida caso os dois valores fossem diferentes um do outro (falha no teste).

  5. Para executar os testes que acabamos de definir, precisamos chamar o método unittest.main(), adicionar as seguintes linhas no módulo “tests.py”.
    filter_none

    editar
    fechar

    play_arrow

    link
    brilho_4
    código

    if __name__ == '__main__':
        unittest.main()
    chevron_right
    
    
    filter_none
    
    

    Por causa dessas linhas, assim que você executar o script “test.py”, a função unittest.main()será chamada e todos os testes serão executados.

Finalmente, o módulo “tests.py” deve ser semelhante ao código fornecido a seguir.



import unittest 
from .. import app 
  
class TestSum(unittest.TestCase): 
  
    def test_area(self): 
        sq = app.Square(2) 
self.assertEqual(sq.area(), 4
            f'Area is shown {sq.area()} rather than 9') 
  
if __name__ == '__main__': 
    unittest.main() 

Tendo escrito nossos casos de teste, vamos agora testar nosso aplicativo para quaisquer bugs. Para testar seu aplicativo, você simplesmente precisa executar o arquivo de teste “tests.py” usando o prompt de comando ou qualquer IDE de sua escolha. A saída deve ser algo assim.

.
-------------------------------------------------- --------------------
Executou 1 teste em 0,000s
Está bem

Na primeira linha, um .(ponto) representa um teste bem-sucedido, enquanto um 'F' representaria um caso de teste com falha. A OKmensagem, no final, nos diz que todos os testes foram aprovados com sucesso.

Vamos adicionar mais alguns testes em “tests.py” e testar novamente nosso aplicativo.

import unittest 
from .. import app 
  
class TestSum(unittest.TestCase): 
  
    def test_area(self): 
        sq = app.Square(2) 
        self.assertEqual(sq.area(), 4
            f'Area is shown {sq.area()} rather than 9') 
  
    def test_area_negative(self): 
        sq = app.Square(-3) 
        self.assertEqual(sq.area(), -1
            f'Area is shown {sq.area()} rather than -1') 
  
    def test_perimeter(self): 
        sq = app.Square(5) 
        self.assertEqual(sq.perimeter(), 20
            f'Perimeter is {sq.perimeter()} rather than 20') 
  
    def test_perimeter_negative(self): 
        sq = app.Square(-6) 
        self.assertEqual(sq.perimeter(), -1
            f'Perimeter is {sq.perimeter()} rather than -1') 
  
if __name__ == '__main__': 
    unittest.main() 
.FF
========================================================== ======================
FALHA: test_area_negative (__main __. TestSum)
-------------------------------------------------- --------------------
Traceback (última chamada mais recente):
  Arquivo "tests_unittest.py", linha 11, em test_area_negative
    self.assertEqual (sq.area(), -1, f'Area é mostrado {sq.area()} em vez de -1 para comprimento do lado negativo ')
AssertionError: 9! = -1: A área é mostrada como 9 em vez de -1 para o comprimento do lado negativo
========================================================== ======================
FALHA: test_perimeter_negative (__main __. TestSum)
-------------------------------------------------- --------------------
Traceback (última chamada mais recente):
  Arquivo "tests_unittest.py", linha 19, em test_perimeter_negative
    self.assertEqual (sq.perimeter(), -1, f'Perimeter is {sq.perimeter()} em vez de -1 para comprimento do lado negativo ')
AssertionError: -24! = -1: Perímetro é -24 em vez de -1 para comprimento do lado negativo
-------------------------------------------------- --------------------
Executou 4 testes em 0,001s
FALHOU (falhas = 2)

Algumas coisas a serem observadas no relatório de teste acima são -

  • A primeira linha representa que o teste 1 e o teste 3 foram executados com sucesso, enquanto o teste 2 e o teste 4 falharam
  • Cada caso de teste com falha é descrito no relatório, a primeira linha da descrição contém o nome do caso de teste com falha e a última linha contém a mensagem de erro que definimos para esse caso de teste.
  • No final do relatório, você pode ver o número de testes com falha, se nenhum teste falhar, o relatório terminará com OK

Nota: Para mais conhecimento, você pode ler a documentação completa do unittest.

O módulo “nariz2”

O objetivo de nose2é estender unittestpara tornar o teste mais fácil. nose2é compatível com testes escritos usando ounittest estrutura de teste e pode ser usado como um substituto do executor de unittestteste.

Instalação: nose2 pode ser instalado a partir do PyPI usando o comando,

pip instalar nariz 2

Uso: nose2 não tem nenhuma estrutura de teste e é meramente um executor de teste compatível com a unittestestrutura de teste. Portanto, vamos executar os mesmos testes que escrevemos acima (para unittest) usandonose2 . Para executar os testes, usamos o seguinte comando no diretório de origem do projeto (“Software_Testing” no nosso caso),

nariz2

Na nose2terminologia, todos os módulos python (.py) com nome começando com “test” (isto é, test_file.py, test_1.py) são considerados como arquivos de teste. Na execução, nose2irá procurar todos os arquivos de teste em todos os subdiretórios que se encontram em uma ou mais das seguintes categorias,

  • que são pacotes python (contêm “__init__.py”).
  • cujo nome começa com “teste” após ser colocado em minúsculas, ou seja, TestFiles, testes.
  • que são nomeados “src” ou “lib”.

nose2primeiro carrega todos os arquivos de teste presentes no projeto e depois os testes são executados. Assim, nose2temos a liberdade de dividir nossos testes entre vários arquivos de teste em pastas diferentes e executá-los ao mesmo tempo, o que é muito útil ao lidar com um grande número de testes.



Agora vamos aprender sobre as diferentes opções de personalização fornecidas pelo nose2que podem nos ajudar durante o processo de teste.

  1. Alterando o diretório de pesquisa -
    Se quisermos alterar o diretório no qual nose2pesquisa os arquivos de teste, podemos fazer isso usando os argumentos da linha de comando-s ou --start-dircomo,
    nariz2 -s DIR_ADD DIR_NAME

    aqui, DIR_NAMEé o diretório no qual desejamos pesquisar os arquivos de teste e, DIR_ADDé o endereço do diretório pai DIR_NAMErelativo ao diretório de origem do projeto (ou seja, use “./” se o diretório de teste estiver no próprio diretório de origem do projeto).
    Isso é extremamente útil quando você deseja testar apenas um recurso de seu aplicativo por vez.

  2. Executando casos de teste específicos -
    Usando nose2, também podemos executar um teste específico por vez usando os argumentos da linha de comando-s e --start-dircomo,
    nariz2 -s DIR_ADD DIR_NAME.TEST_FILE.TEST_CLASS.TEST_NAME
    • TEST_NAME: nome do método de teste.
    • TEST_CLASS: classe na qual o método de teste é definido.
    • TEST_FILE: nome do arquivo de teste no qual o caso de teste é definido, ou seja, test.py.
    • DIR_NAME: diretório no qual o arquivo de teste existe.
    • DIR_ADD: endereço do diretório pai de DIR_NAME relativo à fonte do projeto.

    Usando este recurso, podemos testar nosso software em entradas específicas.

  3. Executando testes em um único módulo -
    nose2 também pode ser usado unittestchamando a função nose2.main()exatamente como chamamos unittest.main()nos exemplos anteriores.
  4. Além das personalizações básicas acima, nose2oferece recursos avançados como carregar vários plug-ins e arquivos de configuração ou criar seu próprio executor de teste.

O módulo “pytest”

pytesté a estrutura de teste mais popular para python. Usando pytestvocê pode testar qualquer coisa, desde scripts python básicos a bancos de dados, APIs e UIs. Embora pytestseja usado principalmente para teste de API, neste artigo vamos cobrir apenas o básico depytest .

Instalação: você pode instalarpytest partir do PyPI usando o comando,

pip install pytest

Use: Opytest executor de teste é chamado usando o seguinte comando na fonte do projeto,

py.test

Ao contrário nose2, pytestprocura por arquivos de teste em todos os locais dentro do diretório do projeto. Qualquer arquivo com o nome começando com “test_” ou terminando com “_test” é considerado um arquivo de teste nopytest terminologia. Vamos criar um arquivo “test_file1.py” na pasta “tests” como nosso arquivo de teste.

Criação de métodos de teste:
pytest oferece suporte aos métodos de teste escritos na unittestestrutura, mas a pytestestrutura fornece uma sintaxe mais fácil para escrever testes. Veja o código abaixo para entender a sintaxe do método de teste da pytestestrutura.

from .. import app 
  
def test_file1_area(): 
    sq = app.Square(2) 
    assert sq.area() == 4
        f"area for side {sq.side} units is {sq.area()}"
  
def test_file1_perimeter(): 
    sq = app.Square(-1) 
    assert sq.perimeter() == -1
        f'perimeter is shown {sq.perimeter()} rather than -1'

Nota: semelhante a unittest, pytestrequer que todos os nomes de teste comecem com “teste”.

Ao contrário unittest, pytestusa as assertinstruções python padrão que o tornam ainda mais fácil de usar.



Observe que agora a pasta “tests” contém dois arquivos, a saber, “tests.py” (escrito no unittestframework) e “test_file1.py” (escrito no pytestframework). Agora vamos executar opytest executor de teste.

py.test

Você obterá um relatório semelhante ao obtido usando unittest.

================================= início da sessão de teste ================== ============
plataforma linux - Python 3.6.7, pytest-4.4.1, py-1.8.0, pluggy-0.9.0
rootdir: / home / manthan / articles / Software_testing_in_Python
coletou 6 itens                                                              
tests / test_file1.py . F                                                    [33%]
testa / test_file2.py . F . F                                                  [100%]
======================================= FALHAS ============== =======================

As porcentagens do lado direito do relatório mostram a porcentagem de testes que foram concluídos naquele momento, ou seja, 2 dos 6 casos de teste foram concluídos no final do “test_file1.py”.

Aqui estão mais algumas personalizações básicas que vêm com pytest.

  1. Executando arquivos de teste específicos: para executar apenas um arquivo de teste específico, use o comando,
    py.test <filename>
  2. Correspondência de substring: suponha que queremos testar apenas o area()método de nossa Squareclasse, podemos fazer isso usando correspondência de substring da seguinte maneira,
    py.test -k "área"

    Com este comando pytestirá executar apenas aqueles testes que possuem a string “area” em seus nomes, ou seja, “test_file1_area()”, “test_area()” etc.

  3. Marcação: como um substituto para a correspondência de substring, a marcação é outro método com o qual podemos executar um conjunto específico de testes. Neste método, colocamos uma marca nos testes que queremos executar. Observe o exemplo de código fornecido abaixo,
    filter_none

    editar
    fechar

    play_arrow

    link
    brilho_4
    código

    @pytest.mark.area     
     
    def test_file1_area():
        sq = app.Square(2)
        assert sq.area() == 4
            f"area for side {sq.side} units is {sq.area()}"
    chevron_right
    
    
    filter_none
    
    

    No exemplo de código acima test_file1_area()está marcado com a tag “área”. Todos os métodos de teste que foram marcados com alguma tag podem ser executados usando o comando,

    py.test -m <tag_name>
  4. Processamento paralelo: se você tiver um grande número de testes, pytestpode ser personalizado para executar esses métodos de teste em paralelo. Para isso, você precisa instalar o pytest-xdistque pode ser instalado usando o comando,
    pip install pytest-xdist

    Agora você pode usar o seguinte comando para executar seus testes mais rapidamente usando multiprocessamento,

    py.test -n 4

    Com este comando pytestatribui 4 trabalhadores para realizar os testes em paralelo, você pode alterar este número de acordo com suas necessidades.

    Se seus testes forem seguros para thread, você também pode usar multithreading para acelerar o processo de teste. Para isso você precisa instalar pytest-parallel(usando pip). Para executar seus testes em multithreading, use o comando,

    pytest --trabalhadores 4