Python unittest:如何仅运行测试文件的一部分?


75

我有一个测试文件,其中包含花费大量时间的测试(它们将计算结果发送到集群并等待结果)。所有这些都在特定的TestCase类中。

由于它们需要时间,而且不太可能中断,因此我希望能够选择测试的该子集是否运行(最好的方法是使用命令行参数,即“ ./tests.py --offline”或其他内容)这样),因此我可以在需要的时候经常且快速地运行大多数测试,并偶尔进行整套测试。

现在,我只是unittest.main()用来开始测试。

谢谢。

Answers:


52

默认值unittest.main()使用默认的测试加载程序从运行main的模块中制作出TestSuite。

您不必使用此默认行为。

例如,您可以制作三个unittest.TestSuite实例。

  1. “快速”子集。

    fast = TestSuite()
    fast.addTests( TestFastThis )
    fast.addTests( TestFastThat )
    
  2. “慢”子集。

    slow = TestSuite()
    slow.addTests( TestSlowAnother )
    slow.addTests( TestSlowSomeMore )
    
  3. “整个”设置。

    alltests = unittest.TestSuite([fast, slow])
    

请注意,我已经对TestCase名称进行了调整,以指示快速与慢速。您可以将unittest.TestLoader子类化以解析类的名称并创建多个加载器。

然后,您的主程序可以使用optparseargparse(从2.7或3.2开始提供)来解析命令行参数,以选择要运行,快速,慢速或全部运行的套件。

或者,您可以信任这sys.argv[1]是三个值之一,并使用像这样简单的值

if __name__ == "__main__":
    suite = eval(sys.argv[1])  # Be careful with this line!
    unittest.TextTestRunner().run(suite)

很好,现在只要在我的算法压力测试中在c ++领域就这么简单:)
Matt Joiner 2010年

@MattJoiner:我认识一个朋友,他使用Python并ctypes针对C / C ++代码编写和运行单元测试。无论如何,这是这个问题的题外话。
丹尼尔森·萨玛

1
我必须修改代码才能使其正常工作。这对我test_class = eval(sys.argv[1]) suite = unittest.TestLoader().loadTestsFromTestCase(test_class) unittest.TextTestRunner().run(suite)
肮脏的企鹅

1
零钱:我认为应该是addTest,而不是addTests。医生说addTests用于测试的迭代,而addTest用于添加TestCase类
Sam Bobel

84

要仅运行单个特定测试,可以使用:

$ python -m unittest test_module.TestClass.test_method

更多信息在这里


1
我正在调试测试用例,因此此方法比公认的答案更有帮助。谢谢。
luanjunyi 2014年

一次也可以进行多个测试。只需确保它们以空格分隔即可,如下所示: python -m unittest test_module.TestClass.test_method test_module.TestClass.test_method2。因此,即使您要运行一些相关的测试用例,这仍然非常有用。
eestrada 2015年

15

我正在使用一个简单的方法skipIf

import os

SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))

@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
    def runTest(self):

这样,我只需要用这一行来装饰一个已经存在的测试用例(不需要创建测试套件或类似的组件,只需os.getenv()在我的单元测试文件的开头创建一个调用行),并且默认情况下会跳过该测试。

如果我想在缓慢的情况下执行它,我可以这样调用我的脚本:

SLOW_TESTS=1 python -m unittest …

12

实际上,可以将测试用例的名称作为sys.argv传递,并且仅对那些用例进行测试。

例如,假设您有

class TestAccount(unittest.TestCase):
    ...

class TestCustomer(unittest.TestCase):
    ...

class TestShipping(unittest.TestCase):
    ...

account = TestAccount
customer = TestCustomer
shipping = TestShipping

你可以打电话

python test.py account

只进行帐户测试,甚至

$ python test.py account customer

对两个案例进行测试


2
在Python 2.7.11和3.5.1中为我工作。名称是模块上可用的属性。account = TestAccount不需要,也可以使用python test.py TestAccount
罗布W

扩展我先前的评论(并说明显而易见的内容):该命令行参数在unittest.main()被调用的情况下有效。EGif __name__ == '__main__': unittest.main()
罗布W¯¯

9

基本上有两种方法可以做到这一点:

  1. 为课程定义自己的测试套件
  2. 创建将返回实际数据的集群连接的模拟类。

我坚决支持他的第二种方法。单元测试应该只测试非常多的代码单元,而不是复杂的系统(例如数据库或集群)。但是我知道这并不总是可能的。有时,创建实体模型太过昂贵了,或者测试的目标实际上是在复杂的系统中。

返回选项(1),您可以按照以下方式进行:

suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))

然后将套件传递给测试运行程序:

unittest.TextTestRunner().run(suite)

有关python文档的更多信息:http : //docs.python.org/library/unittest.html#testsuite-objects


是的,我知道模拟对象,但是我认为这太复杂了。python不会直接连接到集群,而是经过一系列bash脚本,我需要对其行为进行测试。因此,除了最后一个连接部分之外,我需要制作与真实脚本行为相同的“模拟脚本”,但随后我必须同时维护这两个脚本,并确保它们在内容上相同我想测试...感谢您对测试套件的回答,我从S. Lott选择了答案,因为它比较详细,但基本上是相同的。
Gohu

7

由于您使用的是,unittest.main()您可以运行python tests.py --help以获取文档:

Usage: tests.py [options] [test] [...]

Options:
  -h, --help       Show this message
  -v, --verbose    Verbose output
  -q, --quiet      Minimal output
  -f, --failfast   Stop on first failure
  -c, --catch      Catch control-C and display results
  -b, --buffer     Buffer stdout and stderr during test runs

Examples:
  tests.py                               - run default set of tests
  tests.py MyTestSuite                   - run suite 'MyTestSuite'
  tests.py MyTestCase.testSomething      - run MyTestCase.testSomething
  tests.py MyTestCase                    - run all 'test*' test methods
                                               in MyTestCase

也就是说,您只需

python tests.py TestClass.test_method

4

我根据unittest.skip装饰器的工作原理找到了另一个解决方案。通过设置__unittest_skip____unittest_skip_why__

基于标签

我想申请一个标签制度,将某些试验为quickslowglaciermemoryhogcpuhogcore,等。

然后运行all 'quick' testsrun everything except 'memoryhog' tests基本白名单/黑名单设置

实作

我分两部分实现了这一点:

  1. 首先将标签添加到测试中(通过自定义@testlabel类装饰器)
  2. 自定义unittest.TestRunner以标识要跳过的测试,并在执行前修改测试列表的内容。

有效的实现方式在以下要点中:https//gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c

(一个完整的示例太久了,无法放在此处)

结果是...

$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK (skipped=2)

MyTests1因为有foo标签,所以将跳过所有类测试。

--whitelist 也可以


2

或者您可以使用该unittest.SkipTest()功能。例如,skipOrRunTest向您的测试类添加一个方法,如下所示:

def skipOrRunTest(self,testType):
    #testsToRun = 'ALL'
    #testsToRun = 'testType1, testType2, testType3, testType4,...etc'
    #testsToRun = 'testType1'
    #testsToRun = 'testType2'
    #testsToRun = 'testType3'
    testsToRun = 'testType4'              
    if ((testsToRun == 'ALL') or (testType in testsToRun)):
        return True 
    else:
        print "SKIPPED TEST because:\n\t testSuite '" + testType  + "' NOT IN testsToRun['" + testsToRun + "']" 
        self.skipTest("skipppy!!!")

然后,将对这个skipOrRunTest方法的调用添加到每个单元测试的开头,如下所示:

def testType4(self):
    self.skipOrRunTest('testType4')

您可以使用跳过测试修饰,例如@ unittest2.skipUnless(runslowtests(),“ slow test”)
gaoithe

1

考虑使用专用的测试运行程序,例如py.test,nose甚至可能是zope.testing。它们都有用于选择测试的命令行选项。

以鼻子为例:https : //pypi.python.org/pypi/nose/1.3.0


谢谢您的回答,但我认为这有点过头了,所以我选择了TestSuites。
Gohu

1
@GeorgeStocker:您无法使用Google查找新URL?
Lennart Regebro

4
@LennartRegebro您的答案应该单独存在,而无需链接即可完成。链接应该是补充信息。就目前而言,您的答案无法回答问题。更不用说答案404s的有用部分了。参见:meta.stackexchange.com/questions/8231/...
乔治·斯托克

1
@GeorgeStocker:它以一种有用的方式回答了这个问题(即指出,如果您想要这种功能,最好使用扩展unittest的框架),该链接是补充信息。我已经修复了链接。
Lennart Regebro

1

我尝试了@slott的答案:

if __name__ == "__main__":
    suite = eval(sys.argv[1])  # Be careful with this line!
    unittest.TextTestRunner().run(suite)

但这给了我以下错误:

Traceback (most recent call last):
  File "functional_tests.py", line 178, in <module>
    unittest.TextTestRunner().run(suite)
  File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
    test(result)
  File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
    testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string

以下为我工作:

if __name__ == "__main__":
    test_class = eval(sys.argv[1])
    suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
    unittest.TextTestRunner().run(suite)


0

之前还没有找到执行此操作的好方法,因此请在此处共享。

目标:将一组测试文件放在一起,以便它们可以作为一个单元运行,但是我们仍然可以选择其中的任何一个单独运行。

问题:发现方法无法轻松选择要运行的单个测试用例。

设计:请参见下文。这使名称空间变平,因此可以通过TestCase类名称进行选择,并保留“ tests1.test_core”前缀:

./run-tests TestCore.test_fmap

  test_module_names = [
    'tests1.test_core',
    'tests2.test_other',
    'tests3.test_foo',
    ]

  loader = unittest.defaultTestLoader
  if args:
    alltests = unittest.TestSuite()
    for a in args:
      for m in test_module_names:
        try:
          alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
        except AttributeError as e:
          continue
  else:
    alltests = loader.loadTestsFromNames( test_module_names )

  runner = unittest.TextTestRunner( verbosity = opt.verbose )
  runner.run( alltests )

0

这是唯一对我有用的东西。

if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))

当我调用它时,我必须传递类的名称和测试名称。有点不方便,因为我没有记住课程名和测试名的组合。

python ./tests.py class_Name.test_30311

删除类名和测试名将运行文件中的所有测试。我发现这种方法比内置方法更容易处理,因为我实际上并没有在CLI上更改命令。只需添加参数。

享受,基思


0

我创建了一个装饰器,该装饰器允许将测试标记为慢速测试,并使用环境变量跳过它们

from unittest import skip
import os

def slow_test(func):
    return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)

现在您可以将测试标记为缓慢:

@slow_test
def test_my_funky_thing():
    perform_test()

并通过设置SKIP_SLOW_TESTS环境变量来跳过慢速测试:

SKIP_SLOW_TESTS=1 python -m unittest
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.