【问题标题】:How do I get nose to discover dynamically-generated testcases?如何让鼻子发现动态生成的测试用例?
【发布时间】:2010-09-25 17:50:35
【问题描述】:

这是我的previous question 的后续。

在上一个问题中,探索了在整个函数系列中实现基本相同测试的方法,确保测试不会在第一个失败的函数处停止。

我首选的解决方案使用元类将测试动态插入到 unittest.TestCase 中。不幸的是,nose 没有接收到这一点,因为nose 静态扫描测​​试用例。

我如何才能发现并运行这样的 TestCase?请参考here 获取相关TestCase 的示例。

【问题讨论】:

  • 你试过我的解决方案了吗?它不是基于任何魔法,生成的套件应该是可发现的。
  • 我有,请参阅原始线程中的评论。也许它有一个配置选项,或者有什么方法可以为鼻子设置一个“虚拟目标”?
  • 顺便说一句,我已经解决了这个问题,而无需借助鼻子的测试生成器(仅限于您不能将它用于unittest.TestCase 的子类)。见:stackoverflow.com/questions/5176396/…

标签: python unit-testing nose


【解决方案1】:

Nose 有一个“测试生成器”功能来处理这类事情。您编写一个生成器函数,该函数生成您希望它运行的每个“测试用例”函数及其参数。按照您之前的示例,这可以在单独的测试中检查每个功能:

import unittest
import numpy

from somewhere import the_functions

def test_matrix_functions():
    for function in the_functions:
        yield check_matrix_function, function

def check_matrix_function(function)
    matrix1 = numpy.ones((5,10))
    matrix2 = numpy.identity(5)
    output = function(matrix1, matrix2)
    assert matrix1.shape == output.shape, \
           "%s produces output of the wrong shape" % str(function)

【讨论】:

    【解决方案2】:

    Nose 不会静态扫描测​​试,因此您可以使用元类魔法来制作 Nose 找到的测试。

    困难的部分是标准元类技术没有正确设置 func_name 属性,这是 Nose 在检查类上的方法是否为测试时所寻找的。​​p>

    这是一个简单的元类。它查看 func dict 并为它找到的每个方法添加一个新方法,断言它找到的方法有一个文档字符串。这些新的合成方法被命名为"test_%d" %i

    import new
    from inspect import isfunction, getdoc
    
    class Meta(type):
        def __new__(cls, name, bases, dct):
    
            newdct = dct.copy()
            for i, (k, v) in enumerate(filter(lambda e: isfunction(e[1]), dct.items())):
                def m(self, func):
                    assert getdoc(func) is not None
    
                fname = 'test_%d' % i
                newdct[fname] = new.function(m.func_code, globals(), fname,
                    (v,), m.func_closure)
    
            return super(Meta, cls).__new__(cls, 'Test_'+name, bases, newdct)
    

    现在,让我们创建一个使用这个元类的新类

    class Foo(object):
        __metaclass__ = Meta
    
        def greeter(self):
            "sdf"
            print 'Hello World'
    
        def greeter_no_docstring(self):
            pass
    

    在运行时,Foo 实际上将被命名为Test_Foo,并将有greetergreeter_no_docstringtest_1test_2 作为其方法。当我在这个文件上运行nosetests 时,输出如下:

    $ nosetests -v test.py
    test.Test_Foo.test_0 ... FAIL
    test.Test_Foo.test_1 ... ok
    
    ======================================================================
    FAIL: test.Test_Foo.test_0
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
        self.test(*self.arg)
      File "/Users/rmcgibbo/Desktop/test.py", line 10, in m
        assert getdoc(func) is not None
    AssertionError
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.002s
    
    FAILED (failures=1)
    

    这个元类并没有真正有用,但是如果你使用Meta 不是一个适当的元类,而是更多的功能元类(即,将一个类作为参数并返回一个新类,一个重命名以便鼻子会找到它),那么它有用的。作为鼻子测试套件的一部分,我已经使用这种方法自动测试文档字符串是否符合 Numpy 标准。

    另外,我在使用 new.function 正确关闭时遇到了很多麻烦,这就是为什么这段代码使用 m(self, func) ,其中 func 被设为默认参数。在value 上使用闭包会更自然,但这似乎不起作用。

    【讨论】:

      【解决方案3】:

      您可以尝试使用 type() 生成测试用例类

      class UnderTest_MixIn(object):
      
          def f1(self, i):
              return i + 1
      
          def f2(self, i):
              return i + 2
      
      SomeDynamicTestcase = type(
          "SomeDynamicTestcase", 
          (UnderTest_MixIn, unittest.TestCase), 
          {"even_more_dynamic":"attributes .."}
      )
      
      # or even:
      
      name = 'SomeDynamicTestcase'
      globals()[name] = type(
          name, 
          (UnderTest_MixIn, unittest.TestCase), 
          {"even_more_dynamic":"attributes .."}
      )
      

      这应该在鼻子尝试导入您的 test_module 时创建,以便它应该工作。

      这种方法的优点是您可以动态创建多种测试组合。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-31
        • 1970-01-01
        • 1970-01-01
        • 2012-02-29
        • 2012-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多