Python unittest: como executar apenas parte de um arquivo de teste?
-
21-08-2019 - |
Pergunta
Eu tenho um arquivo de teste que contém testes que tomam bastante tempo (eles enviam cálculos para um cluster e espera para o resultado). Todos estes são na classe TestCase específico.
Uma vez que eles levam tempo e, além disso, não são propensos a quebrar, eu gostaria de ser capaz de escolher se este subconjunto de testes faz ou não executar (a melhor maneira seria com um argumento de linha de comando, ou seja " ./tests.py --offline
" ou algo parecido), para que eu pudesse executar a maioria dos testes de frequência e de forma rápida e todo o conjunto de vez em quando, quando eu tiver tempo.
Por enquanto, eu só uso unittest.main()
para iniciar os testes.
Graças.
Solução
O unittest.main()
padrão usa o carregador de teste padrão para fazer um TestSuite fora do módulo no qual principal está em execução.
Você não tem que usar esse comportamento padrão.
Você pode, por exemplo, fazer três casos unittest.TestSuite .
-
O "rápido" subconjunto.
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
-
O "lento" subconjunto.
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
-
O "todo" set.
alltests = unittest.TestSuite([fast, slow])
Note que eu já ajustou os nomes testcase para indicar rápido contra lento. Você pode subclasse unittest.TestLoader para analisar os nomes de classes e criar vários carregadores.
Em seguida, o programa principal pode analisar argumentos de linha de comando com optparse ou argparse (disponível desde 2.7 ou 3.2) para escolher qual conjunto você deseja executar, rápido, lento ou todos.
Ou, você pode confiar que sys.argv[1]
é um dos três valores e usar algo tão simples como isto
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Outras dicas
Para executar apenas um único teste específico você pode usar:
$ python -m unittest test_module.TestClass.test_method
Mais informações aqui
Na verdade, pode-se passar os nomes do caso de teste como sys.argv e apenas os casos serão testados.
Por exemplo, suponha que você tenha
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
Você pode chamar
python test.py account
única para ter em conta os testes, ou mesmo
$ python test.py account customer
para ter ambos os casos testados
Estou fazendo isso usando um skipIf
simples:
import os
SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))
@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
def runTest(self):
…
Desta forma, eu só precisa decorar um caso de teste já existente com esta linha única (não há necessidade de criar suites de teste ou semelhante, assim que a linha chamada um os.getenv()
no início do meu arquivo de teste de unidade), e como um padrão este teste fica ignorado.
Se eu quiser para executá-lo, apesar de ser lento, eu apenas chamar meu script assim:
SLOW_TESTS=1 python -m unittest …
Você tem basicamente duas maneiras de fazer isso:
- Defina o seu próprio conjunto de testes para a classe
- Criar classes de simulação da conexão cluster que irá retornar dados reais.
Eu sou um forte defensor de que ele segunda abordagem; um teste de unidade deve teste apenas uma unidade de código, e não sistemas complexos (como bancos de dados ou clusters). Mas eu entendo que nem sempre é possível; às vezes, criando maquetes é simplesmente demasiado caro, ou o objetivo do teste é realmente no sistema complexo.
Voltar a opção (1), você pode proceder da seguinte maneira:
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
e, em seguida, passar a suite para o corredor de teste:
unittest.TextTestRunner().run(suite)
Mais informações sobre a documentação python: http://docs.python.org /library/unittest.html#testsuite-objects
Uma vez que você usa unittest.main()
você pode simplesmente executar python tests.py --help
para obter a documentação:
Usage: tests.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
tests.py - run default set of tests
tests.py MyTestSuite - run suite 'MyTestSuite'
tests.py MyTestCase.testSomething - run MyTestCase.testSomething
tests.py MyTestCase - run all 'test*' test methods
in MyTestCase
Isto é, você pode simplesmente fazer
python tests.py TestClass.test_method
Ou você pode fazer uso da função unittest.SkipTest()
. Exemplo, adicionar um método skipOrRunTest
à sua classe de teste como este:
def skipOrRunTest(self,testType):
#testsToRun = 'ALL'
#testsToRun = 'testType1, testType2, testType3, testType4,...etc'
#testsToRun = 'testType1'
#testsToRun = 'testType2'
#testsToRun = 'testType3'
testsToRun = 'testType4'
if ((testsToRun == 'ALL') or (testType in testsToRun)):
return True
else:
print "SKIPPED TEST because:\n\t testSuite '" + testType + "' NOT IN testsToRun['" + testsToRun + "']"
self.skipTest("skipppy!!!")
Em seguida, adicione uma chamada para este método skipOrRunTest para o início de cada um dos seus testes de unidade como esta:
def testType4(self):
self.skipOrRunTest('testType4')
Eu encontrei outra solução, com base em como o decorador unittest.skip
funciona.
Ao definir o __unittest_skip__
e __unittest_skip_why__
.
baseado em rótulo
Eu queria aplicar um sistema de rotulagem, para rotular alguns testes como quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, e assim por diante.
all 'quick' tests
Em seguida, executar, ou run everything except 'memoryhog' tests
, sua lista branca básica / setup lista negra
Implementação
Eu implementei este em 2 partes:
- Primeiro adicione rótulos para testes (via um decorador costume classe
@testlabel
) -
unittest.TestRunner
personalizado para identificar quais testes para saltar, e modificar o conteúdo testlist antes de executar.
implementação de Trabalho é neste essência: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c
(a exemplo totalmente funcional era demasiado longo para colocar aqui)
O ser resultado ...
$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=2)
Todos os testes de classe MyTests1
são ignorados porque tem o rótulo foo
.
--whitelist
também funciona
olhar para usar um TestRunner dedicado, como py.test, nariz ou possivelmente até mesmo zope.testing. Todos eles têm opções de linha de comando para seleção de testes.
Olhe por exemplo, como Nariz: https://pypi.python.org/pypi /nose/1.3.0
Eu tentei resposta da @ slott:
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Mas isso me deu o seguinte erro:
Traceback (most recent call last):
File "functional_tests.py", line 178, in <module>
unittest.TextTestRunner().run(suite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string
A seguir trabalhou para mim:
if __name__ == "__main__":
test_class = eval(sys.argv[1])
suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
unittest.TextTestRunner().run(suite)
Eu encontrei outra maneira de selecionar o test_ * métodos que eu só quero correr, adicionando um atributo para eles. Você basicamente usar um metaclass para decorar as funções chamáveis ??dentro da classe TestCase que tem o atributo StepDebug com um decorador unittest.skip. Mais informações sobre
Skipping todos os testes de unidade, mas uma em Python usando decoradores e metaclasses
Eu não sei se é uma solução melhor do que aqueles acima estou apenas fornecendo-lo como uma opção.
não encontrei uma boa maneira de fazer isso antes, assim, compartilhar aqui.
Objetivo: Obter um conjunto de arquivos de teste em conjunto para que eles possam ser executados como uma unidade, mas ainda pode selecionar qualquer um deles para executar por si só.
Problema:. A descobrir método não permite a seleção fácil de um único caso de teste para executar
Desenho: veja abaixo. Este achata ??em> do namespace assim pode selecionar pelo nome da classe TestCase, e deixar de fora o prefixo "tests1.test_core":
./run-tests TestCore.test_fmap
Código
test_module_names = [
'tests1.test_core',
'tests2.test_other',
'tests3.test_foo',
]
loader = unittest.defaultTestLoader
if args:
alltests = unittest.TestSuite()
for a in args:
for m in test_module_names:
try:
alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
except AttributeError as e:
continue
else:
alltests = loader.loadTestsFromNames( test_module_names )
runner = unittest.TextTestRunner( verbosity = opt.verbose )
runner.run( alltests )
Esta é a única coisa que funcionou para mim.
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
Quando liguei para ele que eu tinha que passar o nome do nome da classe e testar. Um pouco inconveniente desde que eu não tenho classe e nome do teste combinação memorizada.
python ./tests.py class_Name.test_30311
A remoção do nome da classe e nome do teste é executado todos os testes em seu arquivo. Acho isso muito mais fácil lidar com o então construído no método desde que eu realmente não mudar o meu comando no CLI. Basta adicionar o parâmetro.
Aproveite, Keith
Eu criei um decorador que permite a marcação testes como testes lentos e ignorá-los usando uma variável de ambiente
from unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
Agora você pode marcar seus testes tão lento como este:
@slow_test
def test_my_funky_thing():
perform_test()
E pular testes lentas, definindo a variável de ambiente SKIP_SLOW_TESTS
:
SKIP_SLOW_TESTS=1 python -m unittest