【问题标题】:Python unittest: how to run only part of a test file?Python unittest:如何只运行测试文件的一部分?
【发布时间】:2010-11-07 06:47:42
【问题描述】:

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

由于它们需要时间而且不太可能中断,我希望能够选择这个测试子集是否运行(最好的方法是使用命令行参数,即“ ./tests.py --offline" 或类似的东西),所以我可以经常快速地运行大部分测试,并在我有时间的时候不时地运行整个测试集。

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

【问题讨论】:

    标签: python unit-testing python-unittest


    【解决方案1】:

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

    python -m unittest test_module.TestClass.test_method
    

    更多信息是here

    【讨论】:

    • 我正在调试我的测试用例,所以这种方法比公认的答案更有帮助。谢谢。
    • 这也适用于一次多个测试。只需确保它们以空格分隔,如下所示:python -m unittest test_module.TestClass.test_method test_module.TestClass.test_method2。因此,即使您有一些相关的测试用例要运行,这仍然非常有用。
    【解决方案2】:

    默认的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 名称以指示 Fast vs. Slow。你可以子类 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++ 领域能这么简单:)
    • @MattJoiner:我认识一个朋友,他使用 Python 和 ctypes 针对 C/C++ 代码编写和运行单元测试。无论如何,这与这个问题无关。
    • 我必须修改代码才能让它工作。这对我有用:test_class = eval(sys.argv[1])suite = unittest.TestLoader().loadTestsFromTestCase(test_class)unittest.TextTestRunner().run(suite)
    • 小改动:我觉得应该是addTest,而不是addTests。文档说 addTests 用于可迭代的测试,而 addTest 用于添加 TestCase 类
    【解决方案3】:

    我正在使用简单的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 …
    

    【讨论】:

      【解决方案4】:

      实际上,可以将测试用例的名称作为 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
      

      测试两种情况

      【讨论】:

      • 在 Python 2.7.11 和 3.5.1 中为我工作。名称是模块上可用的属性。不需要account = TestAccount,也可以使用python test.py TestAccount
      • 扩展我之前的评论(并说明显而易见的):如果调用了unittest.main(),则此命令行参数有效。例如。 if __name__ == '__main__': unittest.main()
      【解决方案5】:

      你基本上有两种方法可以做到:

      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 的回答,因为它更详细一些,但基本上是一样的。
      【解决方案6】:

      由于您使用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
      

      【讨论】:

        【解决方案7】:

        我找到了另一个解决方案,基于 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 也可以使用

        【讨论】:

          【解决方案8】:

          或者您可以使用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(),"慢速测试")
          【解决方案9】:

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

          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
          

          【讨论】:

            【解决方案10】:

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

            例如查看Nose

            【讨论】:

            • 谢谢你的回答,但我觉得有点矫枉过正,所以我选择了TestSuites。
            • @GeorgeStocker:您无法使用 Google 查找新网址?
            • @LennartRegebro 你的答案应该是独立的,不需要链接来完成它。链接应该是补充信息。就目前而言,您的回答并不能回答问题。更不用说答案 404s 的有用部分了。另见:meta.stackexchange.com/questions/8231/…
            • @GeorgeStocker:它以一种有用的方式回答了这个问题(即指出如果你想要这种功能,使用扩展 unittest 的框架是最好的方法),链接是补充信息。我已经修复了链接。
            【解决方案11】:

            我试过S.Lott's answer

            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)
            

            【讨论】:

              【解决方案12】:

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

              问题:discover 方法不允许轻松选择单个测试用例来运行。

              设计:见下文。这扁平化命名空间,因此可以通过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 )
              

              【讨论】:

                【解决方案13】:

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

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

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

                python ./tests.py class_Name.test_30311
                

                删除 class 名称和测试名称会运行文件中的所有测试。我发现这个比内置方法更容易处理,因为我并没有真正更改 CLI 上的命令。只需添加参数即可。

                【讨论】:

                  【解决方案14】:

                  我发现这个答案试图弄清楚如何只运行特定的测试;例如,

                  class TestCase1(unittest.TestCase):
                      def some_test(self):
                          self.assertEqual(True, True)
                  
                  class TestCase2(unittest.TestCase):
                      def some_other_test(self):
                          self.assertEqual(False, False)
                  

                  我想要一种快速注释掉TestCase1TestCase2 的方法,而无需我对 100 多行代码进行扫描,我最终找到了这个方法:

                  if __name__ == "__main__":
                      tests = []
                      tests.append("TestCase1")
                      # tests.append("TestCase2")
                      unittest.main(defaultTest=tests)
                  

                  它只是使用unittest.main()defaultTest 参数来指定要运行的测试类。

                  【讨论】:

                    【解决方案15】:

                    我找到了另一种方法来选择我只想通过向它们添加属性来运行的 test_* 方法。您基本上使用元类来装饰 TestCase 类中的可调用对象,这些对象具有 StepDebug 属性和 unittest.skip 装饰器。更多信息在:

                    Skipping all unit tests but one in Python by using decorators and metaclasses

                    我不知道这是否比上述解决方案更好,我只是将其作为一个选项提供。

                    【讨论】:

                      【解决方案16】:

                      有时我会手动运行每个测试功能。假设我的测试类看起来像这样......

                      class TestStuff(unittest.TestCase):
                          def test1():
                          def test2():
                      

                      然后我运行这个...

                      t = TestStuff()
                      t.test1()
                      t.test2()
                      

                      (我使用 Spyder IDE 进行数据分析,这可能不适合带有更流畅测试工具的 IDE)

                      【讨论】:

                        猜你喜欢
                        • 2017-10-12
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-06-27
                        • 2014-06-16
                        • 2014-06-19
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多