Para onde vão os testes de unidade Python?


490

Se você está escrevendo uma biblioteca ou um aplicativo, para onde vão os arquivos de teste de unidade?

É bom separar os arquivos de teste do código principal do aplicativo, mas é complicado colocá-los em um subdiretório "tests" dentro do diretório raiz do aplicativo, porque fica mais difícil importar os módulos que você testará.

Existe uma prática recomendada aqui?


4
Há muitas respostas aceitáveis ​​para essa pergunta e isso não a torna uma pergunta para um site de perguntas e respostas. As pessoas precisam entender que nem todas as grandes perguntas úteis podem ser feitas no SO. Os formadores escolhem para isso e devemos seguir suas regras.
precisa

121
As pessoas também precisam entender que, quando as páginas SO acabam sendo as principais sucesso de uma pesquisa no Google, talvez seja melhor seguir o fluxo em vez de seguir a doutrina oficial da SO.
22416 Christopher Barber

6
O pessoal do @ChristopherBarber também precisa entender que decisões e fechamento agressivo é o que acabou ganhando uma reputação tão horrenda como um site que eu preferiria não fazer perguntas, a menos que eu realmente não tenha opção melhor.
21418 Stefano Borini

Respostas:


200

Para um arquivo module.py, o teste de unidade deve normalmente ser chamado test_module.py, seguindo as convenções de nomenclatura Pythonic.

Existem vários lugares comumente aceitos para colocar test_module.py:

  1. No mesmo diretório que module.py.
  2. In ../tests/test_module.py(no mesmo nível que o diretório de código).
  3. In tests/test_module.py(um nível no diretório de código).

Eu prefiro o número 1 por sua simplicidade de encontrar os testes e importá-los. Qualquer que seja o sistema de construção que você esteja usando, pode ser facilmente configurado para executar arquivos começando com test_. Na verdade, o padrão unittestpadrão usado para a descoberta de teste étest*.py .


12
O protocolo load_tests procura por arquivos denominados test * .py por padrão. Além disso, este resultado principal do Google e esta documentação mais unida usam o formato test_module.py.
precisa saber é o seguinte

6
O uso de foo_test.py pode salvar um pressionamento de tecla ou dois ao usar o preenchimento de tabulação, pois você não possui um monte de arquivos começando com 'test_'.
precisa saber é o seguinte

11
@ juniper, seguindo seus pensamentos module_test apareceria na conclusão automática quando você estiver codificando. Isso pode ser confuso ou irritante.
Medeiros

11
Ao implantar código, não queremos implantar os testes em nossos locais de produção. Portanto, é fácil tê-los em um diretório './tests/test_blah.py' quando fazemos implantações. Além disso, alguns testes usam arquivos de dados de amostra e é vital ter esses arquivos em um diretório de teste, para que não possamos implantar dados de teste.
22715 Kevin Kevin Rice

1
@ KevinJ.Rice Você não deveria estar testando se o código funciona em seus locais de produção?
endolith

67

Apenas 1 arquivo de teste

Se houver apenas 1 arquivo de teste, é recomendável colocá-lo em um diretório de nível superior:

module/
    lib/
        __init__.py
        module.py
    test.py

Execute o teste na CLI

python test.py

Muitos arquivos de teste

Se houver muitos arquivos de teste, coloque-o em uma testspasta:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

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

Execute o teste na CLI

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

Usar unittest discovery

unittest discovery encontrará todos os testes na pasta do pacote.

Crie uma pasta __init__.pynatests/

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

Execute o teste na CLI

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

Referência

Estrutura de teste de unidade


51

Uma prática comum é colocar o diretório de testes no mesmo diretório pai do seu módulo / pacote. Portanto, se seu módulo foi chamado foo.py, seu layout de diretório seria semelhante a:

parent_dir/
  foo.py
  tests/

Claro que não há uma maneira de fazê-lo. Você também pode criar um subdiretório de testes e importar o módulo usando a importação absoluta .

Onde quer que você faça seus testes, recomendo que você use o nariz para executá-los. O nariz pesquisa nos diretórios por testes. Dessa forma, você pode fazer testes sempre que eles fizerem mais sentido organizacional.


16
Eu gostaria de fazer isso, mas não posso fazê-lo funcionar. Para executar os testes, estou no parent_dir e digite: "python tests \ foo_test.py" e em foo_test.py: "de ..foo importe isso, aquilo, outro" Isso falha com: "ValueError: Tentativa importação relativa no pacote não "parent_dir e tests têm um init .py, então não sei por que eles não são pacotes. Eu suspeito que é porque o script de nível superior que você executa na linha de comando não pode ser considerado (parte de) um pacote, mesmo que esteja em um diretório com um init .py. Então, como faço os testes? Estou trabalhando no Windows hoje, abençoe minhas meias de algodão.
7269 Jonathan Hartley

4
A melhor maneira - descobri - de executar testes de unidade é instalar sua biblioteca / programa e executar testes de unidade com o nariz. Eu recomendaria virtualenv e virtualenvwrapper para tornar isso muito mais fácil.
Cristian

@Tartley - você precisa de um arquivo .py init no diretório 'tests' para que as importações de absolvição funcionem. Eu tenho esse método trabalhando com o nariz, então não sei por que você está tendo problemas.
Cmcginty

4
Obrigado Casey - mas eu tenho um arquivo .py init em todos os diretórios relevantes. Não sei o que faço de errado, mas tenho esse problema em todos os meus projetos em Python e não consigo entender por que mais ninguém sabe. Oh querida.
22139 Jonathan Hartley

8
Uma solução para o meu problema, com Python2.6 ou mais recente, é executar os testes a partir da raiz do seu projeto usando: python -m project.package.tests.module_tests (em vez de python project / package / tests / module_tests.py) . Isso coloca o diretório do módulo de teste no caminho, para que os testes possam fazer uma importação relativa para o diretório pai para obter o módulo em teste.
22411 Jonathan Hartley

32

Tivemos a mesma pergunta ao escrever o Pythoscope ( https://pypi.org/project/pythoscope/ ), que gera testes de unidade para programas Python. Pesquisamos pessoas na lista de testes em python antes de escolhermos um diretório, havia muitas opiniões diferentes. No final, escolhemos colocar um diretório "tests" no mesmo diretório que o código-fonte. Nesse diretório, geramos um arquivo de teste para cada módulo no diretório pai.


2
Impressionante! Acabei de rodar o Pythoscope (ótimo logotipo), em algum código legado e foi incrível. Práticas recomendadas instantâneas e você pode solicitar que outros desenvolvedores preencham os stubs de teste com falha agora. Agora, para ligá-lo em bambu? :)
Will

27

Eu também tendem a colocar meus testes de unidade no próprio arquivo, como observa Jeremy Cantrell acima, embora eu não coloque a função de teste no corpo principal, mas coloque tudo em um

if __name__ == '__main__':
   do tests...

quadra. Isso acaba adicionando documentação ao arquivo como 'código de exemplo' de como usar o arquivo python que você está testando.

Devo acrescentar, tendo a escrever módulos / classes muito restritos. Se seus módulos exigirem um número muito grande de testes, você poderá colocá-los em outro, mas mesmo assim, eu ainda acrescentaria:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

Isso permite que qualquer pessoa que esteja lendo seu código-fonte saiba onde procurar o código de teste.


"como Jeremy Cantrell acima observa" onde?
endolith

Eu gosto dessa maneira de trabalhar. O editor mais simples pode ser configurado para executar seu arquivo com uma tecla de atalho; portanto, quando você estiver visualizando o código, poderá executar os testes em um instante. Infelizmente, em outras linguagens além do Python, isso pode parecer horrível; portanto, se você estiver em uma loja em C ++ ou Java, seus colegas poderão desaprovar isso. Também não funciona bem com ferramentas de cobertura de código.
Keeely

19

De vez em quando eu me pego conferindo o tópico de colocação de teste, e toda vez a maioria recomenda uma estrutura de pastas separada ao lado do código da biblioteca, mas acho que toda vez que os argumentos são os mesmos e não são tão convincentes. Acabo colocando meus módulos de teste em algum lugar ao lado dos módulos principais.

A principal razão para fazer isso é: refatoração .

Quando movo as coisas, quero que os módulos de teste sejam movidos com o código; é fácil perder testes se eles estiverem em uma árvore separada. Sejamos honestos, mais cedo ou mais tarde você acaba com uma estrutura de pastas totalmente diferente, como django , balão e muitas outras. O que é bom se você não se importa.

A principal questão que você deve se perguntar é:

Estou escrevendo:

  • a) biblioteca reutilizável ou
  • b) construir um projeto que agrupe alguns módulos semi-separados?

Se um:

Uma pasta separada e o esforço extra para manter sua estrutura podem ser mais adequados. Ninguém vai reclamar dos seus testes sendo implantados na produção .

Mas também é fácil excluir os testes da distribuição quando eles são misturados às pastas principais; coloque isso no setup.py :

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

Se b:

Você pode desejar - como todos nós - que esteja escrevendo bibliotecas reutilizáveis, mas na maioria das vezes a vida delas está ligada à vida do projeto. A capacidade de manter facilmente seu projeto deve ser uma prioridade.

Então, se você fez um bom trabalho e seu módulo é adequado para outro projeto, ele provavelmente será copiado - não bifurcado ou transformado em uma biblioteca separada - para esse novo projeto e movendo testes que ficam ao lado dele na mesma estrutura de pastas é fácil em comparação com a busca de testes em uma bagunça em que uma pasta de teste separada se tornou. (Você pode argumentar que não deve ser uma bagunça em primeiro lugar, mas vamos ser realistas aqui).

Portanto, a escolha ainda é sua, mas eu argumentaria que, com testes confusos, você realiza as mesmas coisas que em uma pasta separada, mas com menos esforço para manter as coisas organizadas.


1
qual é o problema de implantar testes na produção, na verdade? na minha experiência, é muito útil: permite que os usuários executem testes em seu ambiente ... quando você recebe relatórios de erros, pode pedir ao usuário que execute o conjunto de testes para garantir que está tudo bem e enviar patches para o teste suíte diretamente ... além disso, não é porque você colocar seus testes em module.tests que vai acabar em produção, a menos que você fez algo de errado no seu arquivo de configuração ...
anarcat

2
Como escrevi na minha resposta, normalmente não separo os testes. Mas. Colocar testes no pacote de distribuição pode levar à execução de coisas que você normalmente não desejaria no ambiente de produção (por exemplo, algum código no nível do módulo). É claro que depende de como os testes são escritos, mas para garantir que você os deixe de fora, ninguém executará algo prejudicial por engano. Não sou contra a inclusão de testes em distribuições, mas entendo que, como regra geral, é mais seguro. Colocá-los em uma pasta separada torna super fácil não incluí-los no dist.
Janusz Skonieczny

15

Eu uso um tests/diretório e, em seguida, importo os principais módulos de aplicativos usando importações relativas. Portanto, em MyApp / tests / foo.py, pode haver:

from .. import foo

para importar o MyApp.foomódulo.


5
"ValueError: tentativa de importação relativa em não pacote"
cprn 25/07/16

12

Não acredito que exista uma "melhor prática" estabelecida.

Coloquei meus testes em outro diretório fora do código do aplicativo. Em seguida, adiciono o diretório principal do aplicativo ao sys.path (permitindo importar os módulos de qualquer lugar) no script do executor de teste (que faz outras coisas também) antes de executar todos os testes. Dessa forma, nunca preciso remover o diretório de testes do código principal ao liberá-lo, poupando tempo e esforço, se houver uma quantidade muito pequena.


3
Em seguida, adiciono o diretório principal do aplicativo ao sys.path O problema é que seus testes contêm alguns módulos auxiliares (como o servidor da web de teste) e você precisa importar esses módulos auxiliares dos testes adequados.
Piotr Dobrogost 29/11

Isto é o que parece para mim:os.sys.path.append(os.dirname('..'))
Matt M.

11

Da minha experiência no desenvolvimento de estruturas de teste em Python, sugiro colocar testes de unidade python em um diretório separado. Mantenha uma estrutura de diretório simétrica. Isso seria útil para empacotar apenas as bibliotecas principais e não empacotar os testes de unidade. Abaixo é implementado através de um diagrama esquemático.

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

Dessa maneira, quando você empacota essas bibliotecas usando um rpm, você pode apenas empacotar os módulos principais da biblioteca (apenas). Isso ajuda na manutenção, principalmente em ambientes ágeis.


1
Percebi que uma vantagem potencial dessa abordagem é que os testes podem ser desenvolvidos (e talvez até controlados por versão) como um aplicativo independente. É claro que, para todas as vantagens, há uma desvantagem - manter a simetria é basicamente fazer "contabilidade de dupla entrada" e tornar a refatoração mais uma tarefa árdua.
Jasha

Pergunta de acompanhamento suave: por que o ambiente ágil é particularmente adequado para essa abordagem? (isto é o que acontece com o fluxo de trabalho ágil faz uma estrutura de diretório simétrica tão benéfico?)
Jasha

11

Eu recomendo que você verifique alguns dos principais projetos Python no GitHub e tenha algumas idéias.

Quando seu código aumenta e você adiciona mais bibliotecas, é melhor criar uma pasta de teste no mesmo diretório que você configurou.py e espelhar sua estrutura de diretórios de projeto para cada tipo de teste (mais unittest, integração, ...)

Por exemplo, se você tiver uma estrutura de diretórios como:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

Após adicionar a pasta de teste, você terá uma estrutura de diretórios como:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

Muitos pacotes Python escritos corretamente usam a mesma estrutura. Um exemplo muito bom é o pacote Boto. Verifique https://github.com/boto/boto


1
Recomenda-se usar "testes" em vez de "teste", porque "teste" é um módulo de compilação do Python. docs.python.org/2/library/test.html
brodul

Nem sempre .. por exemplo matplotlib, está em matplotlib/lib/matplotlib/tests( github.com/matplotlib/matplotlib/tree/… ), sklearnem scikitelearn/sklearn/tests( github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests )
alpha_989

7

Como eu faço isso...

Estrutura de pastas:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py aponta para src / como o local que contém meus módulos de projetos e, em seguida, corro:

setup.py develop

O que adiciona meu projeto aos pacotes de sites, apontando para minha cópia de trabalho. Para executar meus testes eu uso:

setup.py tests

Usando o corredor de teste que eu configurei.


Você parece ter substituído o termo "módulo". A maioria dos programadores de Python provavelmente pensaria que o módulo é o arquivo que você chamou code.py. Seria mais sensato chamar o diretório de nível superior "projeto".
blokeley

4

Eu prefiro o diretório de testes de nível superior. Isso significa que as importações se tornam um pouco mais difíceis. Para isso eu tenho duas soluções:

  1. Use as ferramentas de instalação. Então você pode test_suite='tests.runalltests.suite'entrar setup()e executar os testes simplesmente:python setup.py test
  2. Defina PYTHONPATH ao executar os testes: PYTHONPATH=. python tests/runalltests.py

Veja como esse material é suportado pelo código no M2Crypto:

Se você preferir executar testes com testes nos, pode ser necessário fazer algo um pouco diferente.


3

Nós usamos

app/src/code.py
app/testing/code_test.py 
app/docs/..

Em cada arquivo de teste nós inserimos ../src/no sys.path. Não é a melhor solução, mas funciona. Eu acho que seria ótimo se alguém aparecesse com algo como o maven em java que oferece convenções padrão que simplesmente funcionam, não importa em que projeto você trabalha.


1

Se os testes forem simples, basta colocá-los na documentação - a maioria das estruturas de teste do Python poderá usar isso:

>>> import module
>>> module.method('test')
'testresult'

Para outros testes mais envolvidos, eu os colocaria dentro ../tests/test_module.pyou dentro tests/test_module.py.


1

Em C #, geralmente separo os testes em um assembly separado.

No Python - até agora - eu costumava escrever testes de documentos, onde o teste está na cadeia de caracteres de uma função, ou colocá-los no if __name__ == "__main__"bloco na parte inferior do módulo.


0

Ao escrever um pacote chamado "foo", colocarei testes de unidade em um pacote separado "foo_test". Módulos e subpacotes terão o mesmo nome que o módulo do pacote SUT. Por exemplo, testes para um módulo foo.xy são encontrados em foo_test.xy Os arquivos __init__.py de cada pacote de teste contêm um conjunto AllTests que inclui todos os conjuntos de testes do pacote. O setuptools fornece uma maneira conveniente de especificar o pacote de teste principal, para que, após "python setup.py develop", você possa usar "python setup.py test" ou "python setup.py test" ou "python setup.py test -s foo_test.x.SomeTestSuite" para o diretório apenas uma suíte específica.


0

Coloquei meus testes no mesmo diretório que o código em teste (CUT); para foo.pyos testes serão foo_ut.pyiguais ou semelhantes. (Eu ajusto o processo de descoberta de teste para encontrá-los.)

Isso coloca os testes ao lado do código em uma lista de diretórios, tornando óbvio que os testes existem e facilita a abertura dos testes quando possível em um arquivo separado. (Para editores de linha de comando vim foo*e ao usar um navegador gráfico de sistema de arquivos, basta clicar no arquivo CUT e, em seguida, no arquivo de teste imediatamente adjacente.)

Como outros já apontaram , isso também facilita refatorar e extrair o código para uso em outro lugar, caso isso seja necessário.

Eu realmente não gosto da idéia de colocar testes em uma árvore de diretórios completamente diferente; por que dificultar o necessário para os desenvolvedores abrirem os testes quando estão abrindo o arquivo com o CUT? Não é como se a grande maioria dos desenvolvedores estivesse tão interessada em escrever ou ajustar os testes que eles ignorariam qualquer barreira para fazer isso, em vez de usá-la como desculpa. (Muito pelo contrário, na minha experiência; mesmo quando você torna o mais fácil possível, conheço muitos desenvolvedores que não podem se preocupar em escrever testes.)


-2

Recentemente, comecei a programar em Python, então ainda não tive chance de descobrir as melhores práticas. Mas, eu escrevi um módulo que encontra e encontra todos os testes e os executa.

Então eu tenho:

aplicativo/
 appfile.py
teste/
 appfileTest.py

Vou ter que ver como vai o andamento de projetos maiores.


4
Olá, o camelCase é um pouco estranho no mundo python.
Stuart Axon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.