在导入模块之前,nose doctest模块fixture



我使用nose进行测试收集,我还想使用它的doctest插件。我有一个模块需要一个固定装置才能进口。因此,我不能使用nose的模块夹具,因为它们是从测试中的模块加载的。有没有一种方法可以在模块之外为nose-doctest指定模块固定装置?

对于某些用例,可以选择检测是否在doctest下运行,并在模块的开头应用fixture。我也很想听听这个用例的答案。

然而,在某些情况下,这是不起作用的:当导入由于SyntaxError而失败时,将不会运行任何模块代码。在我的情况下,我主要开发与python2和python3兼容的代码(没有2to3)。然而,有一些特定于python 3的模块,当在python 2下运行时,根本不应该通过nose来检查这些模块。我在这里的最佳选择是什么?

编辑:MWE(针对SyntaxError情况)

我有一个包含许多小模块的包,其中一些模块使用python 3语法。以下是包装结构:

~/pckg/
  __init__.py
  py3only.py
  ... (other modules)
  tests/
    test_py3only.py

有些测试是用unittest.TestCase编写的,但我也希望测试文档字符串中的代码示例。~/pckg/__init__.py为空。

~/pkg/py3nly.py:

def fancy_py3_func(a:"A function argument annotation (python 3 only syntax)"):
    """ A function using fancy syntax doubling it's input.
    >>> fancy_py3_func(4)
    8
    """
    return a*2

~/pkg/tests/test_py3nly.py:

import sys, unittest
def setup_module():
    if sys.version_info[0] < 3:
        raise unittest.SkipTest("py3only unavailable on python "+sys.version)
class TestFancyFunc(unittest.TestCase):
    def test_bruteforce(self):
        from pckg.py3only import fancy_py3_func
        for k in range(10):
            self.assertEqual(fancy_py3_func(k),2*k)

在python 3上进行测试,所有内容都经过测试并通过(从封闭文件夹运行,例如~):

~ nosetests3 -v --with-doctest pckg
Doctest: pckg.py3only.fancy_py3_func ... ok
test_bruteforce (test_py3only.TestFancyFunc) ... ok

在python 2上,~/pckg/tests/test_py2only.py的模块fixture正确地检测到了这种情况并跳过了测试。然而,我们从~/pckg/py3only.py:得到一个SyntaxError

~ nosetests -v --with-doctest pckg 
Failure: SyntaxError (invalid syntax (py3only.py, line 1)) ... ERROR
SKIP: py3only unavailable on python 2.7.6 (default, Mar 22 2014, 22:59:56)

如果我能让nose在doctest插件尝试导入该模块之前运行该代码,那么类似于~/pckg/tests/test_py3only.py:setup_module()的函数就可以解决这个问题。

看起来我最好的选择是编写一个适当的顶级测试脚本来处理测试集合。。。

可以使用nose-exclude-nose插件排除特定的测试文件、目录、类或方法。它有--exclude-*选项。

要处理丢失的模块,必须使用mock修补sys.modules

F.e,mycalc模块中有一个Calc类,但我无法访问它,因为它丢失了。另外还有两个模块,mysuper_calcmysuper_calc3,后者是Python 3特有的。这两个模块导入mycalcmysuper_calc3不应该在Python 2下进行测试。如何在纯文本文件中的模块外对它们进行doctest?我想这就是OP的情况。

calc/mysuper_calc3.py

from sys import version_info
if version_info[0] != 3:
    raise Exception('Python 3 required')
from mycalc import Calc
class SuperCalc(Calc):
    '''This class implements an enhanced calculator
    '''
    def __init__(self):
        Calc.__init__(self)
    def add(self, n, m):
        return Calc.add(self, n, m)

calc/mysuper_calc.py

from mycalc import Calc
class SuperCalc(Calc):
    '''This class implements an enhanced calculator
    '''
    def __init__(self):
        Calc.__init__(self)
    def add(self, n, m):
        return Calc.add(self, n, m)

现在模拟mycalc

>>> from mock import Mock, patch
>>> mock = Mock(name='mycalc')

模块mycalc具有类Calc,该类具有方法add。我用2+3测试了SuperCalc实例add方法。

>>> mock.Calc.add.return_value = 5  

现在可以在with块内有条件地导入补丁sys.modulesmysuper_calc3

>>> with patch.dict('sys.modules',{'mycalc': mock}):
...     from mysuper_calc import SuperCalc
...     if version_info[0] == 3:
...         from mysuper_calc3 import SuperCalc

calc/doctest/mysuper_calc_octest.txt

>>> from sys import version_info
>>> from mock import Mock, patch
>>> mock = Mock(name='mycalc')
>>> mock.Calc.add.return_value = 5
>>> with patch.dict('sys.modules',{'mycalc': mock}):
...     from mysuper_calc import SuperCalc
...     if version_info[0] == 3:
...         from mysuper_calc3 import SuperCalc
>>> c = SuperCalc()
>>> c.add(2,3)
5

文件mysuper_calc_doctest.txt必须单独在其自己的目录中,否则nosetests在非测试模块中搜索doctest

PYTHONPATH=.. nosetests --with-doctest --doctest-extension=txt --verbosity=3

文档测试:mysuper_calc_Doctest.txt…确定


在0.038s 中运行1次测试

OK

一个围绕nosetests的包装器,用于检测Python3,该包装器将.py文件无语法错误地传递给nosetests

mynosetests.py

import sys
from subprocess import Popen, PIPE
from glob import glob
f_list = []
py_files = glob('*py')
try:
    py_files.remove(sys.argv[0])
except ValueError:
    pass
for py_file in py_files:
    try:
        exec open(py_file)
    except SyntaxError:
        continue
    else:
        f_list.append(py_file)
proc = Popen(['nosetests'] + sys.argv[1:] + f_list,stdout=PIPE, stderr=PIPE)
print('%sn%s' % proc.communicate())
sys.exit(proc.returncode)

Nose有以下选项:

  --doctest-fixtures=SUFFIX
                    Find fixtures for a doctest file in module with this
                    name appended to the base name of the doctest file

也许您可以将固定装置隔离到一个单独的文件中?

最新更新