我有一个包含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

我似乎有一个某种类型的套件,我可以执行结果。我有点担心这个事实,它说我只运行=1,似乎应该运行=2,但它是进步。但是我如何传递和显示结果的主要?或者说,我怎样才能让它工作,从而运行这个文件,并运行这个目录下的所有单元测试?


当前回答

下面是我通过创建一个包装器来从命令行运行测试的方法:

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

其他回答

这现在可以直接从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)

这是一个老问题,但现在(2019年)对我有用的是:

python -m unittest *_test.py

我的所有测试文件都在与源文件相同的文件夹中,它们以_test结尾。

因为测试发现似乎是一个完整的主题,所以有一些专门的框架来测试发现:

鼻子 Py。测试

更多阅读请点击:https://wiki.python.org/moin/PythonTestingToolsTaxonomy

对于打包的库或应用程序,您不希望这样做。Setuptools将为您做这件事。

要使用此命令,项目的测试必须由函数、TestCase类或方法或包含TestCase类的模块或包包装在unittest测试套件中。如果命名套件是一个模块,并且该模块有一个additional_tests()函数,则调用该函数,并将结果(必须是unittest.TestSuite)添加到要运行的测试中。如果命名套件是一个包,那么任何子模块和子包都会递归地添加到整个测试套件中。

只需要告诉它你的根测试包在哪里,比如:

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

然后运行python setup.py test。

在Python 3中,基于文件的发现可能会有问题,除非您在测试套件中避免相对导入,因为发现使用文件导入。尽管它支持可选的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

我没有包,如本页所述,这是在发布发现时产生的问题。所以,我使用了下面的解决方案。所有测试结果都将放在给定的输出文件夹中。

RunAllUT.py:

"""
The given script is executing all the Unit Test of the project stored at the
path %relativePath2Src% currently fixed coded for the given project. 

Prerequired:
    - Anaconda should be install
    - For the current user, an enviornment called "mtToolsEnv" should exists
    - xmlrunner Library should be installed
"""

import sys
import os
import xmlrunner
from Repository import repository 

relativePath2Src="./../.."
pythonPath=r'"C:\Users\%USERNAME%\.conda\envs\YourConfig\python.exe"' 
outputTestReportFolder=os.path.dirname(os.path.abspath(__file__))+r'\test-reports' #subfolder in current file path

class UTTesting():
    """
    Class tto run all the UT of the project
    """
    def __init__(self):
        """
        Initiate instance

        Returns
        -------
        None.

        """
        self.projectRepository = repository() 
        self.UTfile = [] #List all file
    
    def retrieveAllUT(self):
        """
        Generate the list of UT file in the project

        Returns
        -------
        None.

        """
        print(os.path.realpath(relativePath2Src))
        self.projectRepository.retriveAllFilePaths(relativePath2Src)
        #self.projectRepository.printAllFile() #debug
        for file2scan in self.projectRepository.devfile:
            if file2scan.endswith("_UT.py"):
                self.UTfile.append(file2scan)
                print(self.projectRepository.devfilepath[file2scan]+'/'+file2scan)
                
    
    def runUT(self,UTtoRun):
        """
        Run a single UT

        Parameters
        ----------
        UTtoRun : String
            File Name of the UT

        Returns
        -------
        None.

        """
        print(UTtoRun)
        if UTtoRun in self.projectRepository.devfilepath:
            UTtoRunFolderPath=os.path.realpath(os.path.join(self.projectRepository.devfilepath[UTtoRun]))
            UTtoRunPath = os.path.join(UTtoRunFolderPath, UTtoRun)
        print(UTtoRunPath)
        
        #set the correct execution context & run the test
        os.system(" cd " + UTtoRunFolderPath + \
                  " & " + pythonPath + " " + UTtoRunPath + " " + outputTestReportFolder )
        
        
    def runAllUT(self):
        """
        Run all the UT contained in self
        The function "retrieveAllUT" sjould ahve been performed before

        Returns
        -------
        None.

        """
        for UTfile in self.UTfile:
            self.runUT(UTfile)
                
    
                
if __name__ == "__main__":
    undertest=UTTesting()
    undertest.retrieveAllUT()
    undertest.runAllUT()

在我的特定项目中,我有一个在其他脚本中使用的类。对于您的用例来说,这可能有点过头了。

Repository.py

import os

class repository():
    """
    Class that decribed folder and file in a repository 
    """
    def __init__(self):
        """
        Initiate instance

        Returns
        -------
        None.

        """
        self.devfile = [] #List all file
        self.devfilepath = {} #List all file paths

    def retriveAllFilePaths(self,pathrepo):
        """
        Retrive all files and their path in the class

        Parameters
        ----------
        pathrepo : Path used for the parsin

        Returns
        -------
        None.

        """
        for path, subdirs, files in os.walk(pathrepo):
            for file_name in files:
                self.devfile.append(file_name)
                self.devfilepath[file_name] = path
                
    def printAllFile(self):
        """
        Display all file with paths

        Parameters
        ----------
        def printAllFile : TYPE
            DESCRIPTION.

        Returns
        -------
        None.

        """
        for file_loop in self.devfile:
            print(self.devfilepath[file_loop]+'/'+file_loop)

在你的测试文件中,你需要有一个像这样的main:

if __name__ == "__main__":
    import xmlrunner
    import sys
    
    if len(sys.argv) > 1:
        outputFolder = sys.argv.pop() #avoid conflic with unittest.main
    else:
        outputFolder = r'test-reports'
    print("Report will be created and store there: " + outputFolder)
    
    unittest.main(testRunner=xmlrunner.XMLTestRunner(output=outputFolder))