python - tutorial - Executando o unittest com a estrutura típica de diretórios de teste




unittest python asserts (13)

A estrutura de diretórios muito comum até mesmo para um módulo Python simples parece ser separar os testes de unidade em seu próprio diretório de test :

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

por exemplo, veja este howto do projeto Python .

Minha pergunta é simplesmente Qual é a maneira usual de executar os testes? Eu suspeito que isso seja óbvio para todos, exceto para mim, mas você não pode simplesmente executar python test_antigravity.py partir do diretório de teste, pois sua import antigravity falhará, pois o módulo não está no caminho.

Eu sei que eu poderia modificar o PYTHONPATH e outros truques relacionados ao caminho de busca, mas não posso acreditar que seja a maneira mais simples - tudo bem se você for o desenvolvedor, mas não realista esperar que seus usuários usem se quiserem verificar se os testes são passagem.

A outra alternativa é apenas copiar o arquivo de teste para o outro diretório, mas parece um pouco estúpido e perde o ponto de tê-los em um diretório separado para começar.

Então, se você tivesse acabado de baixar a fonte para o meu novo projeto, como você executaria os testes de unidade? Eu prefiro uma resposta que me permita dizer aos meus usuários: "Para executar os testes unitários do X."


Qual é a maneira usual de executar os testes?

Eu uso o Python 3.6.2

cd new_project

pytest test/test_antigravity.py

Para instalar o pytest : sudo pip install pytest

Não defini nenhuma variável de caminho e minhas importações não estão falhando com a mesma estrutura de projeto de "teste".

Eu comentei estas coisas: if __name__ == '__main__' assim:

test_antigravity.py

import antigravity

class TestAntigravity(unittest.TestCase):

    def test_something(self):

        # ... test stuff here


# if __name__ == '__main__':
# 
#     if __package__ is None:
# 
#         import something
#         sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
#         from .. import antigravity
# 
#     else:
# 
#         from .. import antigravity
# 
#     unittest.main()

Python 3+

Adicionando a @Pierre

Usando a estrutura de diretório unittest assim:

new_project
├── antigravity
   ├── __init__.py         # make it a package
   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

Para executar o módulo de teste test_antigravity.py :

$ cd new_project
$ python -m unittest test.test_antigravity

Ou um único TestCase

$ python -m unittest test.test_antigravity.GravityTestCase

Obrigatório , não esqueça o __init__.py mesmo se vazio, caso contrário não funcionará.


A melhor solução na minha opinião é usar a interface de linha de comando unittest que irá adicionar o diretório ao sys.path assim você não precisa (feito na classe TestLoader ).

Por exemplo, para uma estrutura de diretórios como esta:

new_project
├── antigravity.py
└── test_antigravity.py

Você pode apenas executar:

$ cd new_project
$ python -m unittest test_antigravity

Para uma estrutura de diretórios como a sua:

new_project
├── antigravity
   ├── __init__.py         # make it a package
   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

E nos módulos de teste dentro do pacote de test , você pode importar o pacote antigravity e seus módulos como de costume:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

Executando um único módulo de teste:

Para executar um único módulo de teste, neste caso test_antigravity.py :

$ cd new_project
$ python -m unittest test.test_antigravity

Apenas faça referência ao módulo de teste da mesma maneira que você o importa.

Executando um único caso de teste ou método de teste:

Além disso, você pode executar um único TestCase ou um único método de teste:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

Executando todos os testes:

Você também pode usar a descoberta de teste que irá descobrir e executar todos os testes para você, eles devem ser módulos ou pacotes chamados test*.py (podem ser alterados com o sinalizador -p, --pattern ):

$ cd new_project
$ python -m unittest discover

Isso executará todos os módulos test*.py test dentro do pacote de test .


A seguir, a estrutura do meu projeto:

ProjectFolder:
 - project:
     - __init__.py
     - item.py
 - tests:
     - test_item.py

Eu achei melhor importar no método setUp ():

import unittest
import sys    

class ItemTest(unittest.TestCase):

    def setUp(self):
        sys.path.insert(0, "../project")
        from project import item
        # further setup using this import

    def test_item_props(self):
        # do my assertions

if __name__ == "__main__":
    unittest.main()

Do artigo que você vinculou a:

Crie um arquivo test_modulename.py e coloque seus testes unittest nele. Como os módulos de teste estão em um diretório separado do seu código, você pode precisar adicionar o diretório pai do seu módulo ao seu PYTHONPATH para executá-los:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

Finalmente, há mais uma estrutura de teste de unidade popular para Python (é tão importante!), Nariz. O nose ajuda a simplificar e estender o framework unittest embutido (ele pode, por exemplo, automagicamente encontrar seu código de teste e configurar seu PYTHONPATH para você), mas ele não está incluído na distribuição padrão do Python.

Talvez você deva olhar para o nose como sugere?


Este script BASH executará o diretório de teste do teste unitário do python de qualquer lugar no sistema de arquivos, não importa em qual diretório de trabalho você esteja.

Isso é útil quando se está no diretório de trabalho ./example ou ./example e você precisa de um teste de unidade rápido:

#!/bin/bash

this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

Não há necessidade de um arquivo test/__init__.py sobrecarregar seu pacote / sobrecarga de memória durante a produção.


Geralmente, crio um script de "testes de execução" no diretório do projeto (aquele que é comum ao diretório de origem e ao test ) que carrega meu conjunto "Todos os testes". Normalmente, esse é um código clichê, então posso reutilizá-lo de projeto para projeto.

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test / all_tests.py (de Como executo todos os testes de unidade do Python em um diretório? )

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

Com essa configuração, você pode include antigravity apenas include antigravity em seus módulos de teste. A desvantagem é que você precisaria de mais código de suporte para executar um teste em particular ... Eu apenas os executo todas as vezes.


Notei que, se você executar a interface de linha de comando unittest a partir do diretório "src", as importações funcionarão corretamente sem modificação.

python -m unittest discover -s ../test

Se você quiser colocar isso em um arquivo de lote no seu diretório de projeto, você pode fazer isso:

setlocal & cd src & python -m unittest discover -s ../test

Se você executar o "python setup.py develop", o pacote estará no caminho. Mas você pode não querer fazer isso porque pode infectar a instalação do sistema python, e é por isso que existem ferramentas como virtualenv e buildout .


Se você tiver vários diretórios em seu diretório de teste, será necessário incluir em cada diretório um arquivo __init__.py .

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

Então, para executar todos os testes de uma só vez, execute:

python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil

Fonte: python -m unittest -h

  -s START, --start-directory START
                        Directory to start discovery ('.' default)
  -t TOP, --top-level-directory TOP
                        Top level directory of project (defaults to start
                        directory)

Use o setup.py develop para tornar o seu diretório de trabalho parte do ambiente Python instalado, depois execute os testes.


Você deve realmente usar a ferramenta pip.

Use pip install -e para instalar seu pacote no modo de desenvolvimento. Esta é uma prática muito boa.

Na url de referência fornecida abaixo, dois projetos clássicos (com teste) são fornecidos, você pode seguir qualquer um deles.

Ref :


Solução / Exemplo para o módulo unittest do Python

Dada a seguinte estrutura de projeto:

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

Você pode executar seu projeto a partir do diretório raiz com python project_name , que chama ProjectName/project_name/__main__.py .

Para executar seus testes com o python test , efetivamente executando ProjectName/test/__main__.py , você precisa fazer o seguinte:

1) Transforme seu diretório test/models em um pacote adicionando um arquivo __init__.py . Isso torna os casos de teste dentro do subdiretório acessíveis a partir do diretório de test pai.

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2) Modifique o caminho do seu sistema em test/__main__.py para incluir o diretório project_name .

# ProjectName/test/__main__.py

import sys
import unittest

sys.path.append('../project_name')

loader = unittest.TestLoader()
testSuite = loader.discover('test')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)

Agora você pode importar com sucesso as coisas do project_name nos seus testes.

# ProjectName/test/models/test_thing_1.py    

import unittest
from project_name.models import Thing1  # this doesn't work without 'sys.path.append' per step 2 above

class Thing1TestCase(unittest.TestCase):

    def test_thing_1_init(self):
        thing_id = 'ABC'
        thing1 = Thing1(thing_id)
        self.assertEqual(thing_id, thing.id)




unit-testing