Executando teste único a partir do unittest.TestCase via linha de comando

257

Em nossa equipe, definimos a maioria dos casos de teste como este:

Uma classe "framework" ourtcfw.py:

import unittest

class OurTcFw(unittest.TestCase):
    def setUp:
        # something

    # other stuff that we want to use everywhere

e muitos casos de teste como testMyCase.py:

import localweather

class MyCase(OurTcFw):

    def testItIsSunny(self):
        self.assertTrue(localweather.sunny)

    def testItIsHot(self):
        self.assertTrue(localweather.temperature > 20)

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

Quando estou escrevendo um novo código de teste e quero executá-lo com frequência e economizar tempo, o que faço é colocar "__" na frente de todos os outros testes. Mas é complicado, me distrai do código que estou escrevendo e o ruído de confirmação que isso cria é claramente irritante.

Então, por exemplo, ao fazer alterações testItIsHot(), quero poder fazer isso:

$ python testMyCase.py testItIsHot

e unittestcorreram apenas testItIsHot()

Como posso conseguir isso?

Tentei reescrever a if __name__ == "__main__":peça, mas como sou novo no Python, estou me sentindo perdido e continuo me baseando em tudo o que não seja nos métodos.

Alois Mahdal
fonte

Respostas:

311

Isso funciona como você sugere - você também precisa especificar o nome da classe:

python testMyCase.py MyCase.testItIsHot
phihag
fonte
2
Oh meu! Como os testes devem ser executados no python2.6 (99% do tempo eu posso testar os próprios testes com python2.7), eu estava olhando para o doc 2.6.8 e perdi muito! :-)
Alois Mahdal 12/04
1
Só notei que isso funciona somente se o método é chamado de "teste *", então, infelizmente, não pode ser usado para testar ocasionalmente executar que é "deficiente" por renomeação
Alois Mahdal
4
Não funciona para testes em um subdiretório - o caso mais comum em um programa Python maduro.
Tom Swirly
4
@ TomSwirly Não é possível verificar agora, mas acho que você pode fazê-lo criando (vazio) __init__.pydentro desse diretório (e subdividindo, se houver) e chamando, por exemplo. python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal
1
Nada acontece quando eu faço isso. Encontrei soluções alternativas, mas esperava que esse método funcionasse para mim.
Joe Flack
152

Se você organizar seus casos de teste, siga a mesma organização como o código real e também use importações relativas para módulos no mesmo pacote

Você também pode usar o seguinte formato de comando:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

Documentação do Python3 para isso: https://docs.python.org/3/library/unittest.html#command-line-interface

Ajay M
fonte
Isso é tão desajeitadamente Java. "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ... melhor espero que você não tenha modularizado em suítes como uma pessoa sã que testa seu código.
Joshua Detwiler
69

Pode funcionar bem como você imagina

python testMyCase.py MyCase.testItIsHot

E há outra maneira de apenas testar testItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)
Yarkee
fonte
11
Achei a segunda parte desta resposta extremamente útil: estou escrevendo testes no Eclipse + PyDev e não quero mudar para a linha de comando!
Giovanni Di Milia 15/10
25

Se você verificar a ajuda do módulo mais unido, ele informará sobre várias combinações que permitem executar classes de caso de teste de um módulo e métodos de teste de uma classe de caso de teste.

python3 -m unittest -h

[...]

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method

Não requer que você defina a unittest.main()como o comportamento padrão do seu módulo.

skqr
fonte
2
+1 e como a terminologia pode ser confusa se for nova em um idioma (e o usageé até estranhamente inconsistente): running python -m unittest module_test.TestClass.test_methodassume um arquivo module_test.py(executado no diretório atual; e não__init.py__ é necessário); e contém que contém (isso também funciona para mim em python 2.7.13)module_test.pyclass TestClass(unittest.TestCase)...def test_method(self,...)
michael
11

Talvez seja útil para alguém. Caso você queira executar apenas testes de uma classe específica:

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

Funciona para mim no python 3.6

Bohdan
fonte
3

Inspirado por @yarkee , combinei-o com parte do código que já recebi. Você também pode chamar isso de outro script, apenas chamando a função run_unit_tests()sem a necessidade de usar a linha de comando ou apenas a partir da linha de comando python3 my_test_file.py.

import my_test_file
my_test_file.run_unit_tests()

Infelizmente isso só funciona para Python 3.3ou superior:

import unittest

class LineBalancingUnitTests(unittest.TestCase):

    @classmethod
    def setUp(self):
        self.maxDiff = None

    def test_it_is_sunny(self):
        self.assertTrue("a" == "a")

    def test_it_is_hot(self):
        self.assertTrue("a" != "b")

Código do corredor:

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from .somewhere import LineBalancingUnitTests

def create_suite(classes, unit_tests_to_run):
    suite = unittest.TestSuite()
    unit_tests_to_run_count = len( unit_tests_to_run )

    for _class in classes:
        _object = _class()
        for function_name in dir( _object ):
            if function_name.lower().startswith( "test" ):
                if unit_tests_to_run_count > 0 \
                        and function_name not in unit_tests_to_run:
                    continue
                suite.addTest( _class( function_name ) )
    return suite

def run_unit_tests():
    runner = unittest.TextTestRunner()
    classes =  [
        LineBalancingUnitTests,
    ]

    # Comment all the tests names on this list, to run all Unit Tests
    unit_tests_to_run =  [
        "test_it_is_sunny",
        # "test_it_is_hot",
    ]
    runner.run( create_suite( classes, unit_tests_to_run ) )

if __name__ == "__main__":
    print( "\n\n" )
    run_unit_tests()

Editando um pouco o código, você pode passar em uma matriz com todos os testes de unidade que deseja chamar:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

    runner.run( suite( classes, unit_tests_to_run ) )
...

E outro arquivo:

import my_test_file

# Comment all the tests names on this list, to run all Unit Tests
unit_tests_to_run = \
[
    "test_it_is_sunny",
    # "test_it_is_hot",
]

my_test_file.run_unit_tests( unit_tests_to_run )

Como alternativa, você pode usar https://docs.python.org/3/library/unittest.html#load-tests-protocol e definir o seguinte método no seu módulo / arquivo de teste:

def load_tests(loader, standard_tests, pattern):
    suite = unittest.TestSuite()

    # To add a single test from this file
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )

    # To add a single test class from this file
    suite.addTests( unittest.TestLoader().loadTestsFromTestCase( LineBalancingUnitTests ) )

    return suite

Se você deseja limitar a execução a um único arquivo de teste, basta definir o padrão de descoberta de teste como o único arquivo em que você definiu a load_tests()função.

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest

test_pattern = 'mytest/module/name.py'
PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )

loader = unittest.TestLoader()
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

suite = loader.discover( start_dir, test_pattern )
runner = unittest.TextTestRunner( verbosity=2 )
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )

sys.exit( not results.wasSuccessful() )

Referências:

  1. Problema com sys.argv [1] quando o módulo mais unido está em um script
  2. Existe uma maneira de percorrer e executar todas as funções em uma classe Python?
  3. fazendo um loop sobre todas as variáveis ​​de membro de uma classe em python

Como alternativa ao último exemplo de programa principal, criei a seguinte variação depois de ler a unittest.main()implementação do método:

  1. https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L65
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import unittest

PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

from testing_package import main_unit_tests_module
testNames = ["TestCaseClassName.test_nameHelloWorld"]

loader = unittest.TestLoader()
suite = loader.loadTestsFromNames( testNames, main_unit_tests_module )

runner = unittest.TextTestRunner(verbosity=2)
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )
do utilizador
fonte
3

TL; DR : Isso provavelmente funcionaria:

python mypkg/tests/test_module.py MyCase.testItIsHot

A explicação :

  • A maneira conveniente

    python mypkg/tests/test_module.py MyCase.testItIsHot

    funcionaria, mas sua suposição tácita é que você já possui esse trecho de código convencional dentro (normalmente no final de) do seu arquivo de teste.

    if __name__ == "__main__":
        unittest.main()
  • A maneira inconveniente

    python -m unittest mypkg.tests.test_module.TestClass.test_method

    sempre funcionaria, sem exigir que você tenha esse if __name__ == "__main__": unittest.main() trecho de código no arquivo de origem do teste.

Então, por que o segundo método é considerado inconveniente? Porque seria uma dor no (_ insira uma parte do seu corpo aqui _) digitar manualmente esse caminho longo e delimitado por pontos. Enquanto no 1º método, a mypkg/tests/test_module.pypeça pode ser concluída automaticamente, por um shell moderno ou pelo seu editor.

PS: Se você pensou que essa parte do corpo está em algum lugar abaixo da sua cintura, você é uma pessoa autêntica. :-) Quero dizer "articulação do dedo". Digitar em excesso seria ruim para as articulações. ;-)

RayLuo
fonte