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


315

파이썬 단위 테스트가 들어있는 디렉토리가 있습니다. 각 단위 테스트 모듈의 형식은 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에 어떻게 전달하고 표시합니까? 아니면 기본적으로 어떻게 작동하게해서이 파일을 실행할 수 있고, 그렇게 할 때이 디렉토리에서 모든 단위 테스트를 실행합니까?


1
Python 2.7 이상을 사용하는 경우 Travis의 답변으로 건너 뛰십시오
Rocky

테스트 인스턴스 객체에서 테스트를 실행 한 적이 있습니까?
피노키오

예제 파일 구조의 솔루션에 대해서는 이 답변 을 참조하십시오 .
Derek Soike

답변:


477

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

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

python 2.7 또는 python 3.x unittest 문서 에서 더 많은 내용을 읽을 수 있습니다 .


11
ImportError : 시작 디렉토리를 가져올 수 없습니다 :
zinking

6
적어도 Linux에서 Python 2.7.8을 사용하면 명령 줄 호출로 재귀를 제공하지 않습니다. 내 프로젝트에는 단위 테스트가 각각의 "unit_tests / <subproject> / python /"디렉토리에있는 여러 개의 하위 프로젝트가 있습니다. 이러한 경로를 지정하면 해당 하위 프로젝트에 대한 단위 테스트가 실행되지만 테스트 디렉토리 인수로 "unit_tests"만 있으면 테스트를 찾을 수 없습니다 (원하는대로 모든 하위 프로젝트에 대한 모든 테스트 대신). 힌트가 있습니까?
user686249

6
재귀 정보 : <test_directory>가없는 첫 번째 명령의 기본값은 "."입니다. 하위 모듈로 되풀이됩니다 . 즉, 검색하려는 모든 테스트 디렉토리에는 init .py 가 있어야합니다 . 그렇게하면 discover 명령에 의해 발견됩니다. 방금 시도해 보았습니다.
Emil Stenström 2016 년

이것은 나를 위해 일했습니다. 4 개의 파일이있는 테스트 폴더가 있으며 Linux 터미널 에서이 폴더를 실행하십시오.
JasTonAChair

5
감사! 이것이 왜 대답이 맞지 않습니까? 제 생각에는, 더 나은 대답은 항상 외부 의존성이 필요하지 않은 것입니다.
Jonathan Benn

108

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

업데이트 :

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

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)

2
이 접근법의 장점은 모든 테스트 모듈을 명시 적으로 하나의 test_all.py 모듈로 가져오고 unittest.main ()을 호출하여 다른 모듈이 아닌 일부 모듈에서 테스트 슈트를 선택적으로 선언 할 수 있다는 것입니다.
Corey Porter

1
나는 코를 시험해 보았고 완벽하게 작동합니다. 내 프로젝트에서 설치하고 실행하기가 쉬웠습니다. 나는 virtualenv 내부에서 실행되는 몇 줄의 스크립트로 자동화 할 수도있었습니다. 코 +1!
Jesse Webb

항상 가능하지는 않습니다. 때때로 프로젝트의 가져 오기 구조가 모듈에서 가져 오기를 실행하려고하면 혼란스러워 질 수 있습니다.
chiffa

4
참고 코는 "지난 몇 년 동안 유지 보수 모드"되고있어, 현재 사용하는 것이 좋습니다 nose2 , pytest , 또는 그냥 일반 유닛 테스트 / unittest2 새로운 프로젝트.
Kurt Peek

테스트 인스턴스 객체에서 테스트를 실행 한 적이 있습니까?
피노키오

96

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

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

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

python -m unittest

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


3
기본적으로 "test"로 시작하는 파일 이름의 테스트 만 검색합니다.
Shawabawa

3
맞습니다. 원래의 질문은 "각 단위 테스트 모듈은 test _ *. py 형식입니다."라는 사실을 언급 한 것이므로이 답변은 직접 답장입니다. 나는 더 명확하게 답변을 업데이트했다
tmck-code

1
고마워, Travis Bear의 대답을 사용하기 위해 누락 된 것이 있습니다.
Jeremy Cochoy 2014

65

이제 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)

3
나는이 방법도 시도했지만 몇 가지 테스트를했지만 완벽하게 작동합니다. 우수한!!! 그러나 나는 4 가지 테스트 만 가지고있는 것이 궁금합니다. 함께 0.032를 실행하지만이 방법을 사용하여 모두 실행하면 결과가 표시됩니다. .... ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK왜 그렇습니까? 차이점은 어디에서 왔습니까?
simkus

명령 줄에서 다음과 같은 파일을 실행하는 데 문제가 있습니다. 어떻게 호출해야합니까?
Dustin Michels

python file.py
slaughter98

1
완벽하게 일했습니다! test / dir에 설정 한 다음 start_id = "./"설정하십시오. IMHO,이 답변은 이제 (Python 3.7) 받아 들여졌습니다!
jjwdesign 2012 년

마지막 줄을 ´res = runner.run (suite);로 변경할 수 있습니다. sys.exit (0 res.wasSuccessful () else 1)) 올바른 종료 코드를 원하는 경우
Sadap

32

글쎄, 위의 코드 (특히 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)

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


좋아, 그것은 현재 디렉토리에서 잘 작동합니다. 하위를 직접 호출하는 방법은 무엇입니까?
Larry Cai

래리, 재귀 테스트 발견에 대한 새로운 답변 ( stackoverflow.com/a/24562019/104143 )
Peter Kofler

테스트 인스턴스 객체에서 테스트를 실행 한 적이 있습니까?
Pinocchio

25

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

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.


로부터 unittest.TestLoader 워드 프로세서 : "일반적으로,이 클래스의 인스턴스를 만들 필요가 없습니다, 유닛 테스트 모듈이 unittest.defaultTestLoader으로 공유 할 수있는 인스턴스를 제공합니다." 또한 iterable 을 인수로 TestSuite허용하므로 반복 을 피하기 위해 루프에서 iterable을 구성 할 수 있습니다 loader.loadTestsFromTestCase.
2 비트 연금술사

@ 2 비트 연금술사 두 번째 요점은 훌륭합니다. 포함하도록 코드를 변경했지만 테스트 할 수 없습니다. (첫 번째 모드는 내 취향에 따라 Java와 너무 비슷하게 보일 것입니다. 비록 비이성적이라는 것을 알지만 (그들의 낙타 케이스 변수 이름을 조이십시오)).
demented hedgehog

이것은 내가 좋아하는 것, 매우 깨끗합니다. 이것을 패키징하여 일반 명령 줄에서 인수로 만들 수있었습니다.
MarkII

15

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

이것은 단지 python2.7 사용할 것 같아요
래리 카이

@larrycai 어쩌면 나는 보통 Python 3, 때로는 Python 2.7에 있습니다. 질문은 특정 버전과 관련이 없습니다.
rds

저는 Python 3.4를 사용하고 있으며 스위트를 반환하여 루프를 중복시킵니다.
모래 언덕

미래의 Larry 's : "테스트 발견을 포함하여 Python 2.7에서 unittest에 많은 새로운 기능이 추가되었습니다. unittest2 를 사용하면 이러한 기능을 이전 버전의 Python에서 사용할 수 있습니다."
2 비트 연금술사

8

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

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

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


7

(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

좋은 대답이며 배포하기 전에 테스트를 자동화하는 데 사용할 수 있습니다! 감사합니다
아서 클레르 - GHERARDI

4

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프로젝트 설정에 따라 인수를 변경해야 할 수도 있습니다 .


모든 테스트 파일 및 테스트 방법의 이름은 "test_"로 시작해야합니다. 그렇지 않으면 "Run as-> Python unit test"명령으로 찾을 수 없습니다.
Stefan

2

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).


2

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

python -m unittest *_test.py

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



1

이 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파일을 제작할 때 패키지 / 메모리 오버 헤드에 부담을 줄 필요가 없습니다 .


-3

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

#!/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

2
이 답변의 대부분은 테스트 검색 (예 : 로깅 등)과 관련이 없습니다. 스택 오버플로는 관련이없는 코드를 보여주지 않고 질문에 답변하기위한 것입니다.
Corey Goldberg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.