如何在Python中无须增加代码来基准测试单元测试

6 投票
1 回答
4814 浏览
提问于 2025-04-18 09:20

我有一个Python项目,里面已经写了一堆测试,现在我想开始对这些测试进行基准测试,这样我就可以比较代码、服务器等在一段时间内的性能。找到这些测试文件其实不难,因为我所有的测试文件名里都有“test”这个词。不过,我在尝试动态执行这些测试时遇到了一些麻烦。

目前,我能运行一个脚本,这个脚本接受一个目录路径作为参数,然后返回一个文件路径的列表,像这样:

def getTestFiles(directory):
    fileList = []
    print "Searching for 'test' in " + directory
    if not os.path.isdir(os.path.dirname(directory)):
        # throw error
        raise InputError(directory, "Not a valid directory")
    else:
        for root, dirs, files in os.walk(directory):
            #print files
            for f in files:
                if "test" in f and f.endswith(".py"):
                    fileList.append(os.path.join(root, f))
    return fileList

# returns a list like this:
# [  'C:/Users/myName/Desktop/example1_test.py',
#    'C:/Users/myName/Desktop/example2_test.py',
#    'C:/Users/myName/Desktop/folder1/example3_test.py',
#    'C:/Users/myName/Desktop/folder2/example4_test.py'...  ]

问题是,这些文件的语法可能不同,我正在想办法处理这个问题。例如:

TestExampleOne:

import dummy1
import dummy2
import dummy3

class TestExampleOne(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        # set up

    def test_one(self):
        # test stuff
    def test_two(self):
        # test stuff
    def test_three(self):
        # test stuff

    # etc...

TestExampleTwo:

import dummy1
import dummy2
import dummy3

def setup(self):
    try:
        # config stuff
    except Exception as e:
        logger.exception(e)

def test_one():
    # test stuff
def test_two():
    # test stuff
def test_three():
    # test stuff

# etc...

TestExampleThree:

import dummy1
import dummy2
import dummy3

def setup(self):
    try:
        # config stuff
    except Exception as e:
        logger.exception(e)

class TestExampleTwo(unittest.TestCase):
    def test_one(self):
        # test stuff
    def test_two(self):
        # test stuff
    # etc...

class TestExampleThree(unittest.TestCase):
    def test_one(self):
        # test stuff
    def test_two(self):
        # test stuff
    # etc...

# etc...

我真的希望能写一个模块,去搜索一个目录,找到所有文件名里包含“test”的文件,然后执行每个文件里的每个单元测试,并提供每个测试的执行时间。我觉得像NodeVisitor这样的东西可能是个不错的方向,但我不太确定。即使是一个开始的想法也会非常感谢。谢谢!

1 个回答

14

使用 nose 测试工具可以帮助我们 发现测试,以及设置和清理函数。

nose-timer 插件可以帮助我们进行性能测试:

这是一个用于 nosetests 的计时插件,能回答一个问题:每个测试花费了多少时间?


演示:

  • 假设你有一个名为 test_nose 的包,里面有以下脚本:

    • test1.py:

      import time
      import unittest
      
      class TestExampleOne(unittest.TestCase):
          @classmethod
          def setUpClass(cls):
              cls.value = 1
      
          def test_one(self):
              time.sleep(1)
              self.assertEqual(1, self.value)
      
    • test2.py:

      import time
      
      value = None
      
      def setup():
          global value
          value = 1
      
      def test_one():
          time.sleep(2)
          assert value == 1
      
    • test3.py:

      import time
      import unittest
      
      value = None
      
      def setup():
          global value
          value = 1
      
      class TestExampleTwo(unittest.TestCase):
          def test_one(self):
              time.sleep(3)
              self.assertEqual(1, value)
      
      class TestExampleThree(unittest.TestCase):
          def test_one(self):
              time.sleep(4)
              self.assertEqual(1, value)
      
  • 安装 nose 测试工具:

    pip install nose
    
  • 安装 nose-timer 插件:

    pip install nose-timer
    
  • 运行测试:

    $ nosetests test_nose --with-timer
    ....
    test_nose.test3.TestExampleThree.test_one: 4.0003s
    test_nose.test3.TestExampleTwo.test_one: 3.0010s
    test_nose.test2.test_one: 2.0011s
    test_nose.test1.TestExampleOne.test_one: 1.0005s
    ----------------------------------------------------------------------
    Ran 4 tests in 10.006s
    
    OK
    

结果会很方便地被高亮显示:

nosetests results

颜色可以通过 --timer-ok--timer-warning 参数来控制。

注意,time.sleep(n) 的调用是为了手动增加延迟,以便清楚地看到影响。同时,value 变量在 "setup" 函数和方法中被设置为 1,然后在测试函数和方法中,value 被断言为 1 - 这样你就可以看到设置函数的工作效果。

更新(从脚本中运行 nosenose-timer):

from pprint import pprint
import nose
from nosetimer import plugin

plugin = plugin.TimerPlugin()
plugin.enabled = True
plugin.timer_ok = 1000
plugin.timer_warning = 2000
plugin.timer_no_color = False


nose.run(plugins=[plugin])
result = plugin._timed_tests
pprint(result)

将其保存到 test.py 脚本中,并传入目标目录:

python test.py /home/example/dir/tests --with-timer

result 变量将包含:

{'test_nose.test1.TestExampleOne.test_one': 1.0009748935699463,
 'test_nose.test2.test_one': 2.0003929138183594,
 'test_nose.test3.TestExampleThree.test_one': 4.000233173370361,
 'test_nose.test3.TestExampleTwo.test_one': 3.001115083694458}

撰写回答