关于单元测试:Python unittest:如何只运行部分测试文件?

Python unittest: how to run only part of a test file?

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

因为它们需要时间,而且不可能中断,所以我希望能够选择这部分测试是运行还是不运行(最好的方法是使用命令行参数,例如"./tests.py --offline"或类似的参数),这样当我有时间的时候,我可以经常、快速地运行大多数测试,以及运行整个测试集。

目前,我只使用unittest.main()开始测试。

谢谢。


要只运行一个特定的测试,您可以使用:

1
$ python -m unittest test_module.TestClass.test_method

此处提供更多信息


默认的unittest.main()使用默认的测试加载程序从运行main的模块中生成一个测试套件。

您不必使用此默认行为。

例如,您可以创建三个UnitTest.TestSuite实例。

  • "快速"子集。

    1
    2
    3
    fast = TestSuite()
    fast.addTests( TestFastThis )
    fast.addTests( TestFastThat )
  • "慢"子集。

    1
    2
    3
    slow = TestSuite()
    slow.addTests( TestSlowAnother )
    slow.addTests( TestSlowSomeMore )

  • 全套的。

    1
    alltests = unittest.TestSuite([fast, slow])
  • 请注意,我已经调整了测试用例名称,以指示快速与缓慢。您可以子类unittest.testloader解析类名称并创建多个加载程序。

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

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

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


    我用一个简单的skipIf来完成:

    1
    2
    3
    4
    5
    6
    7
    8
    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()调用行),默认情况下,这个测试会被跳过。

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

    1
    SLOW_TESTS=1 python -m unittest

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

    例如,假设您

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class TestAccount(unittest.TestCase):
        ...

    class TestCustomer(unittest.TestCase):
        ...

    class TestShipping(unittest.TestCase):
        ...

    account = TestAccount
    customer = TestCustomer
    shipping = TestShipping

    你可以打电话

    1
    python test.py account

    只有帐户测试,甚至

    1
    $ python test.py account customer

    对两个病例进行检测


    你基本上有两种方法:

  • 为类定义自己的测试套件
  • 创建将返回实际数据的集群连接的模拟类。
  • 我是第二种方法的强烈支持者;单元测试应该只测试一个非常单元的代码,而不是复杂的系统(如数据库或集群)。但我理解这并不总是可能的;有时,创建实体模型太贵了,或者测试的目标确实在复杂的系统中。

    回到选项(1),您可以这样继续:

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

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

    1
    unittest.TextTestRunner().run(suite)

    关于python文档的更多信息:http://docs.python.org/library/unittest.html testsuite对象


    由于您使用了unittest.main(),因此您只需运行python tests.py --help即可获得文档:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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

    也就是说,你可以

    1
    python tests.py TestClass.test_method


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

    基于标签

    我想应用一个标记系统,将一些测试标记为quickslowglaciermemoryhogcpuhogcore等。

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

    实施

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

  • 首先向测试添加标签(通过自定义@testlabel类装饰器)
  • 自定义unittest.TestRunner以标识要跳过的测试,并在执行前修改测试列表内容。
  • 工作实施在此要点中:https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51a2ebb3f6c

    (一个充分发挥作用的例子太长了,不能放在这里)

    结果是…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ ./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也起作用。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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:
    \t testSuite '"
    + testType  +"' NOT IN testsToRun['" + testsToRun +"']"
            self.skipTest("skipppy!!!")

    然后在每个单元测试的开始添加对skiporruntest方法的调用,如下所示:

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


    我试过了@slott的回答:

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

    但这给了我以下错误:

    1
    2
    3
    4
    5
    6
    7
    8
    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

    以下内容对我很有用:

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


    研究使用专用的测试运行程序,比如py.test、nose甚至zope.testing。它们都有用于选择测试的命令行选项。

    例如nose:https://pypi.python.org/pypi/nose/1.3.0


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

    1
    2
    3
    4
    5
    from unittest import skip
    import os

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

    现在,您可以将测试标记为慢,如下所示:

    1
    2
    3
    @slow_test
    def test_my_funky_thing():
        perform_test()

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

    1
    SKIP_SLOW_TESTS=1 python -m unittest


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

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

    当我调用它时,尽管我必须以类和测试名称的名义通过。有点不方便,因为我没有记住班级和考试名称的组合。

    python./tests.py类u name.test u 30311

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

    享受,基思


    以前没有找到一个很好的方法,所以在这里分享。

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

    问题:发现方法不允许简单地选择单个测试用例来运行。

    设计:见下文。这会使命名空间变平,以便可以按testcase类名选择,并去掉"tests1.test_core"前缀:

    1
    ./run-tests TestCore.test_fmap

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
      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 )


    我找到了另一种选择测试方法的方法,我只想通过向它们添加一个属性来运行这些方法。基本上,您使用一个元类来修饰具有stepDebug属性的testcase类内部的可调用文件,该类使用一个unittest.skip修饰器。有关的详细信息

    通过使用decorator和元类跳过python中除一个以外的所有单元测试

    我不知道它是否比上面的那些更好的解决方案,我只是作为一个选项提供它。