IT

디렉토리에서 모든 Python 단위 테스트를 어떻게 실행합니까?

lottoking 2020. 4. 1. 08:16
반응형

디렉토리에서 모든 Python 단위 테스트를 어떻게 실행합니까?


파이썬 단위 테스트가 들어있는 디렉토리가 있습니다. 각 단위 테스트 모듈의 형식은 test _ *. py 입니다. all_test.py 라는 파일을 만들려고합니다.이 파일 은 위에서 언급 한 테스트 양식으로 모든 파일을 실행하고 결과를 반환합니다. 지금까지 두 가지 방법을 시도했습니다. 둘 다 실패했습니다. 나는 두 가지 방법을 보여줄 것이며, 누군가가 실제로 이것을 올바르게 수행하는 방법을 알고 있기를 바랍니다.

첫 번째 용감한 시도를 위해 "파일에서 모든 테스트 모듈을 가져온 다음이 unittest.main()doodad 를 호출 하면 제대로 작동합니까?"라고 생각했습니다. 글쎄, 내가 틀렸다는 것이 밝혀졌다.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]

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

이것은 효과가 없었습니다. 결과는 다음과 같습니다.

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

내 두 번째 시도를 위해, 그래도, 나는 아마도이 전체 테스트 작업을보다 "수동적 인"방식으로 시도 할 것입니다. 그래서 아래에서 시도했습니다.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite 

result = unittest.TestResult()
testSuite.run(result)
print result

#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __name__ == "__main__":
    unittest.main()

이것은 또한 작동하지 않았지만 너무 가깝습니다!

$ python all_test.py 
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

나는 일종의 스위트를 가지고있는 것처럼 보이고 결과를 실행할 수 있습니다. 나는 그것이 단지 내가 가지고 있다고 말하는 것에 대해 조금 걱정하고 run=1있습니다 run=2. 그러나 결과를 main에 어떻게 전달하고 표시합니까? 아니면 기본적으로 어떻게 작동하게해서이 파일을 실행할 수 있고, 그렇게 할 때이 디렉토리에서 모든 단위 테스트를 실행합니까?


이를 위해 테스트 러너를 사용할 수 있습니다. 예를 들어 는 매우 좋습니다. 실행되면 현재 트리에서 테스트를 찾아 실행합니다.

업데이트 :

코 이전의 코드가 있습니다. 모듈 이름의 명시적인 목록을 원하지 않을 수도 있지만 나머지는 유용 할 것입니다.

testmodules = [
    'cogapp.test_makefiles',
    'cogapp.test_whiteutils',
    'cogapp.test_cogapp',
    ]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        # If the module defines a suite() function, call it to get the suite.
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        # else, just load all the test cases from the module.
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

Python 2.7 이상에서는이를 위해 새 코드를 작성하거나 타사 도구를 사용할 필요가 없습니다. 명령 행을 통한 재귀 테스트 실행이 내장되어 있습니다.

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'

python 2.7 또는 python 3.x unittest 문서 에서 자세한 내용을 읽을 수 있습니다 .


파이썬 3에서 다음을 사용하는 경우 unittest.TestCase:

  • 디렉토리 __init__.py비어 있거나 다른 파일이 있어야 합니다 (이름을 지정 해야 합니다 ).testtest/
  • 내부의 테스트 파일 test/은 패턴과 일치합니다 test_*.py. 이들은 아래의 서브 디렉토리 안에있을 수 있으며 해당 서브 디렉토리 test/는 무엇이든 지정할 수 있습니다.

그런 다음 다음을 사용하여 모든 테스트를 실행할 수 있습니다.

python -m unittest

끝난! 100 줄 미만의 솔루션. 다른 파이썬 초보자가 이것을 찾아서 시간을 절약하기를 바랍니다.


이제 unittest : unittest.TestLoader.discover 에서 직접 가능합니다 .

import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)

글쎄, 위의 코드 (특히 TextTestRunnerand 사용 defaultTestLoader) 를 연구함으로써 꽤 가까워 질 수있었습니다. 결국 나는 모든 테스트 스위트를 "수동으로"추가하는 대신 단일 스위트 생성자에 전달하여 다른 문제를 해결함으로써 코드를 수정했습니다. 그래서 여기 내 해결책이 있습니다.

import glob
import unittest

test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)

네, 아마도 코를 사용하는 것이이 일을하는 것보다 쉬울 것입니다. 그러나 그것은 요점입니다.


다양한 테스트 케이스 클래스에서 모든 테스트를 실행하고 명시 적으로 지정하려면 다음과 같이 할 수 있습니다.

from unittest import TestLoader, TextTestRunner, TestSuite
from uclid.test.test_symbols import TestSymbols
from uclid.test.test_patterns import TestPatterns

if __name__ == "__main__":

    loader = TestLoader()
    tests = [
        loader.loadTestsFromTestCase(test)
        for test in (TestSymbols, TestPatterns)
    ]
    suite = TestSuite(tests)

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

어디에 uclid내 프로젝트 및 TestSymbolsTestPatterns의 서브 클래스입니다 TestCase.


discover방법 을 사용하여 load_tests코드의 (최소한, 생각하는) 숫자 줄로이 결과를 달성했습니다.

def load_tests(loader, tests, pattern):
''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/``
'''
    suite = TestSuite()
    for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'):
        for test_suite in all_test_suite:
            suite.addTests(test_suite)
    return suite

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

5-5에서 실행

Ran 27 tests in 0.187s
OK

나는 다양한 접근 방식을 시도했지만 모두 결함이있는 것처럼 보이거나 코드를 작성해야합니다. 그러나 리눅스에는 확실한 패턴이 있습니다. 즉, 특정 패턴을 통해 모든 테스트를 찾은 다음 하나씩 테스트하는 것입니다.

find . -name 'Test*py' -exec python '{}' \;

가장 중요한 것은 확실히 작동합니다.


(A)의 경우 패키지 라이브러리 나 응용 프로그램, 당신은하고 싶지 않아. setuptools 당신을 위해 그것을 할 것 입니다.

이 명령을 사용하려면 unittest함수, TestCase 클래스 또는 메소드 또는 TestCase클래스를 포함하는 모듈 또는 패키지로 프로젝트의 테스트를 테스트 스위트에 랩핑해야합니다 . 명명 된 제품군이 모듈이고 모듈에 additional_tests()기능 이있는 경우 해당 모듈 이 호출되고 결과 (이어야 함 unittest.TestSuite)가 실행될 테스트에 추가됩니다. 명명 된 제품군이 패키지 인 경우 모든 하위 모듈과 하위 패키지가 전체 테스트 제품군에 재귀 적으로 추가됩니다 .

루트 테스트 패키지가 어디에 있는지 알려주십시오.

setup(
    # ...
    test_suite = 'somepkg.test'
)

그리고 실행하십시오 python setup.py test.

파일 기반 검색 discover은 파일 가져 오기를 사용 하기 때문에 테스트 스위트에서 상대적 가져 오기를 피하지 않으면 Python 3에서 문제가 될 수 있습니다 . optional을 지원 top_level_dir하지만 무한 재귀 오류가 발생했습니다. 따라서 패키지화되지 않은 코드에 대한 간단한 해결책은 다음을 __init__.py테스트 패키지 에 넣는 것입니다 ( load_tests 프로토콜 참조 ).

import unittest

from . import foo, bar


def load_tests(loader, tests, pattern):
    suite = unittest.TestSuite()
    suite.addTests(loader.loadTestsFromModule(foo))
    suite.addTests(loader.loadTestsFromModule(bar))

    return suite

PyDev / LiClipse를 사용하고 실제로 GUI에서 모든 테스트를 한 번에 실행하는 방법을 찾지 못했습니다. (편집 : 루트 테스트 폴더를 마우스 오른쪽 버튼으로 클릭하고Run as -> Python unit-test

이것은 현재 해결 방법입니다.

import unittest

def load_tests(loader, tests, pattern):
    return loader.discover('.')

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

이 코드를 all테스트 디렉토리 에있는 모듈에 넣었습니다 . LiClipse에서이 모듈을 단위 테스트로 실행하면 모든 테스트가 실행됩니다. 특정 또는 실패한 테스트 만 반복하도록 요청하면 해당 테스트 만 실행됩니다. 내 명령 줄 테스트 러너를 방해하지 않으며 무시하지 않습니다.

discover프로젝트 설정에 따라 인수를 변경해야 할 수도 있습니다 .


Stephen Cagle 의 답변에 따라 중첩 테스트 모듈에 대한 지원을 추가했습니다.

import fnmatch
import os
import unittest

def all_test_modules(root_dir, pattern):
    test_file_names = all_files_in(root_dir, pattern)
    return [path_to_module(str) for str in test_file_names]

def all_files_in(root_dir, pattern):
    matches = []

    for root, dirnames, filenames in os.walk(root_dir):
        for filename in fnmatch.filter(filenames, pattern):
            matches.append(os.path.join(root, filename))

    return matches

def path_to_module(py_file):
    return strip_leading_dots( \
        replace_slash_by_dot(  \
            strip_extension(py_file)))

def strip_extension(py_file):
    return py_file[0:len(py_file) - len('.py')]

def replace_slash_by_dot(str):
    return str.replace('\\', '.').replace('/', '.')

def strip_leading_dots(str):
    while str.startswith('.'):
       str = str[1:len(str)]
    return str

module_names = all_test_modules('.', '*Tests.py')
suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname 
    in module_names]

testSuite = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=1)
runner.run(testSuite)

이 코드 .*Tests.py파일 의 모든 하위 디렉토리를 검색 한 다음로드합니다. *Tests.py클래스에는 *Tests(unittest.TestCase)차례로로드되어 차례로 실행되는 단일 클래스가 포함될 것으로 예상됩니다 .

이것은 디렉토리 / 모듈의 임의의 깊은 중첩에서 작동하지만 그 사이의 각 디렉토리에는 __init__.py최소한 파일이 있어야합니다. 이렇게하면 테스트에서 슬래시 (또는 백 슬래시)를 점으로 대체하여 중첩 모듈을로드 할 수 있습니다 (참조 replace_slash_by_dot).


테스트 디스커버리는 완전한 주제 인 것처럼 보이기 때문에 디스커버리를 테스트하기위한 전용 프레임 워크가 있습니다.

자세한 내용은 여기 : https://wiki.python.org/moin/PythonTestingToolsTaxonomy


이 BASH 스크립트는 파일 시스템의 어느 디렉토리에 있든지 상관없이 파일 시스템의 모든 위치에서 python unittest 테스트 디렉토리를 실행합니다. 작업 디렉토리는 항상 해당 test디렉토리가있는 위치입니다.

모든 테스트, 독립적 인 $ PWD

unittest Python 모듈은 ( discover -s옵션을 사용하여 ) 어디에 지시하지 않는 한 현재 디렉토리에 민감합니다 .

이것은 ./src또는 ./example작업 디렉토리에 머무를 때 유용 하며 빠른 전체 단위 테스트가 필요합니다.

#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

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

선택된 테스트, 독립 $ PWD

이 유틸리티 파일의 이름을 다음 runone.py과 같이 사용하십시오.

runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

(cd "$dirname"/test; python -m unittest $1)

test/__init__.py파일을 제작할 때 패키지 / 메모리 오버 헤드에 부담을 줄 필요가 없습니다 .


이것은 오래된 질문이지만 2019 년 현재 나를 위해 일한 것은 다음과 같습니다.

python -m unittest *_test.py

모든 테스트 파일은 소스 파일과 동일한 폴더에 있으며로 끝납니다 _test.


다음은 명령 행에서 테스트를 실행하기 위한 랩퍼작성 하는 방법입니다 .

#!/usr/bin/env python3
import os, sys, unittest, argparse, inspect, logging

if __name__ == '__main__':
    # Parse arguments.
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument("-d", "--debug",    action="store_true", dest="debug",    help="show debug messages" )
    parser.add_argument("-h", "--host",     action="store",      dest="host",     help="Destination host" )
    parser.add_argument("-b", "--browser",  action="store",      dest="browser",  help="Browser driver.", choices=["Firefox", "Chrome", "IE", "Opera", "PhantomJS"] )
    parser.add_argument("-r", "--reports-dir", action="store",   dest="dir",      help="Directory to save screenshots.", default="reports")
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    # Load files from the arguments.
    for filename in args.files:
        exec(open(filename).read())

    # See: http://codereview.stackexchange.com/q/88655/15346
    def make_suite(tc_class):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(tc_class)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(tc_class(name, cargs=args))
        return suite

    # Add all tests.
    alltests = unittest.TestSuite()
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj) and name.startswith("FooTest"):
            alltests.addTest(make_suite(obj))

    # Set-up logger
    verbose = bool(os.environ.get('VERBOSE', args.verbose))
    debug   = bool(os.environ.get('DEBUG', args.debug))
    if verbose or debug:
        logging.basicConfig( stream=sys.stdout )
        root = logging.getLogger()
        root.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
        root.addHandler(ch)
    else:
        logging.basicConfig(stream=sys.stderr)

    # Run tests.
    result = unittest.TextTestRunner(verbosity=2).run(alltests)
    sys.exit(not result.wasSuccessful())

간단하게하기 위해 PEP8 이외의 코딩 표준을 변명하십시오 .

그런 다음 모든 테스트의 공통 구성 요소에 대한 BaseTest 클래스를 만들 수 있으므로 각 테스트는 다음과 같습니다.

from BaseTest import BaseTest
class FooTestPagesBasic(BaseTest):
    def test_foo(self):
        driver = self.driver
        driver.get(self.base_url + "/")

실행하려면 간단히 명령 줄 인수의 일부로 테스트를 지정하십시오.

./run_tests.py -h http://example.com/ tests/**/*.py

참고 URL : https://stackoverflow.com/questions/1732438/how-do-i-run-all-python-unit-tests-in-a-directory

반응형