디렉토리에서 모든 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내 프로젝트 및 TestSymbols및 TestPatterns의 서브 클래스입니다 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.