如何运行表单test / a.py的unitests?

时间:2018-05-06 17:44:56

标签: python unit-testing testing project

是否可以使用如下文件结构实现Python项目?:

myproj
├── a.py
├── b.py
├── c.py
└── test/
    ├── a.py
    ├── b.py
    └── c.py

请特别注意,test/下的测试脚本与他们正在测试 1 的模块文件具有相同的基本名称。 (换句话说,test/a.py包含a.py的单元测试; test/b.py包含b.py等的单元测试。)

test/下的测试全部导入unittest并定义unittest.TestCase的子类。

我想知道如何在test/下运行测试,无论是单独还是一起运行。

我尝试了python -m unittest ...的许多变体,但它们都失败了(下面的示例),或最终运行零测试。

例如,

% python -m unittest test.a
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 100, in loadTestsFromName
    parent, obj = obj, getattr(obj, part)
AttributeError: 'module' object has no attribute 'a'

如果我将test/目录的名称更改为t/,则错误变为:

% python -m unittest t.a
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
ImportError: No module named t

或者

% python -m unittest t/a.py
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
ImportError: Import by filename is not supported.

(我使用的是Python 2.7.9。)

更新

由于我对这个问题给予了赏识,我将非常明确地说明什么是可接受的答案。

以下任一项都可以接受:

  • 如何从命令行调用unittest来运行单个测试或test/目录下的所有测试;解决方案可以接受对测试脚本中的代码进行小的更改(例如修改import语句)。

  • 如果出于某种原因无法使用上面显示的文件结构,详细说明将是可接受的解决方案。

作为基本案例,从以下最小案例开始,使用以下文件结构:

myproj
├── a.py
├── b.py
└── test/
    ├── a.py
    └── b.py

......以及以下内容

# a.py
def hello():
    print 'hello world'
# b.py
def bye():
    print 'good-bye world'
# test/a.py

import unittest
import a

class TestA(unittest.TestCase):
    def test_hello(self):
        self.assertEqual(a.hello(), None)
# test/b.py

import unittest
import b

class TestB(unittest.TestCase):
    def test_bye(self):
        self.assertEqual(b.bye(), None)

显示如何告诉unittest运行测试test/a.py,以及如何运行“test下的所有测试”。 (后者应继续工作,即使新的测试脚本已添加到test,或者某些当前的测试脚本已被删除。)

迄今为止提供的建议的最低限度测试表明它们不起作用。例如:

% python -m unittest discover -s test -p '*.py'
EE
======================================================================
ERROR: test_hello (a.TestA)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/SHIVAMJINDAL/myproj/test/a.py", line 6, in test_hello
    self.assertEqual(a.hello(), None)
AttributeError: 'module' object has no attribute 'hello'

======================================================================
ERROR: test_bye (b.TestB)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/SHIVAMJINDAL/myproj/test/b.py", line 6, in test_bye
    self.assertEqual(b.bye, None)
AttributeError: 'module' object has no attribute 'bye'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=2)
% tree .
.
├── a.py
├── b.py
├── __init__.py
└── test/
    ├── a.py
    ├── b.py
    └── __init__.py

1 directory, 6 files

1 这个约束是非常有意的,它是这里提出的问题的一个组成部分。 (IOW,需要放松这种约束的“解决方案”实际上不是解决方案。)

8 个答案:

答案 0 :(得分:7)

我能够使您的方法有效,但几乎没有必要和强制性的更改。

  1. 应使用父目录上下文运行测试
  2. 主文件夹和测试文件夹都应该有try (ASN1InputStream asn1InputStream = new ASN1InputStream(k.getEncoded())) { DERObject rsaPrivateKey = asn1InputStream.readObject(); return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption), rsaPrivateKey).getDEREncoded(); }
  3. 测试中的导入应该是相对导入而不是直接导入
  4. 以下是我的树形结构

    __init__.py

    <强>项目/ a.py

    root@5db7ad85dafd:/project# tree
    .
     __init__.py
     a.py
     test
         __init__.py
         a.py
    
    1 directory, 4 files
    
    root@5db7ad85dafd:/project# python --version
    Python 2.7.9
    

    <强>项目/测试/ a.py

    hello = 'tarun'
    

    注意导入的import unittest from .. import a class TestStringMethods(unittest.TestCase): def test_abc(self): assert a.hello == "tarun"

    接下来,我们将测试项目放在项目的根文件夹中,如下所示

    from .. import a

    root@5db7ad85dafd:/project# python -m unittest discover -t .. -s test -p "*.py" . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK 这里设置了顶级导入目录,因此我们的相对导入可以工作 -t告诉我们测试所在的目录 -s告诉我们应该在

    中发现测试的模式

    当您想要进行单独的测试时,您将执行以下操作

    -p

    python -m unittest discover -t .. -s test -p "a.py"
    

    图片总是比文字更值得

    Unit Tests working

    修改-1

    在看到彼得的回答之后想要更新我的回答。我没有提到从固定命名包导入的原因是,这意味着您需要知道克隆代码的文件夹的名称,并强制保持相同。但是如果你仍然想要采用这种方法,那么一种方法是将实际移动到子文件夹

    所以它会是python -m unittest discover -t .. -s test -p "*.py" a ,然后在你的测试中你将使用

    repo/project/test/a.py

    然后从repo文件夹

    运行它,如下所示
    from project import a
    

    或类似下面的测试文件夹

    root@5db7ad85dafd:/repo# python -m unittest discover -v -t project -s project.test -p "*.py"
    test_abc (test.a.TestStringMethods) ... ok
    
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    
    OK
    

    在这种情况下,将项目文件夹从根目录移动一级,将确保项目名称不依赖于克隆项目的文件夹

答案 1 :(得分:2)

塔伦的答案已经相当完整......简而言之:

  1. 您必须使您的模块可导入(通过创建__init__.py文件)
  2. 您必须告诉解释器确切的位置,以防止模块之间的命名冲突。
  3. 但是,导入不一定是相对的。假设您的项目将被打包,导入可以(可以说,应该)正如您期望的包的用户使用 - 例如from myproj import a

    此时我还有python -m unittest discover -t .. -s test -p '*.py'工作。但是这就是我厌倦了基本的unittest包放在用户身上的额外箍。我还建议您进行2次更改后,还会安装nosetests(严格来说是python nose包),因为它通常可以让您更轻松地查找和运行测试。

    例如:

    $ tree
    .
    ├── a.py
    ├── a.pyc
    ├── b.py
    ├── b.pyc
    ├── __init__.py
    ├── __init__.pyc
    └── test
        ├── a.py
        ├── a.pyc
        ├── b.py
        ├── b.pyc
        ├── __init__.py
        └── __init__.pyc
    
    1 directory, 12 files
    $ cat test/a.py
    # test/a.py
    
    import unittest
    from myproj import a
    
    class TestA(unittest.TestCase):
        def test_hello(self):
            self.assertEqual(a.hello(), None)
    $ nosetests test/*.py
    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 0.001s
    
    OK
    

答案 2 :(得分:1)

  

是否可以使用文件结构实现Python项目   喜欢以下几个?

我不认为这种结构可以按照你的方式使用。在这个apporach中,根本问题是测试中的模块名称冲突。

例如,您的测试用例中的语句import a&#39; test \ a.py&#39;解析为模块test\a而不是引用测试中的模块(a.py)

由于您希望将测试模块命名为与模块下的模块完全相同,因此可以通过一种可能的解决方案将所有模块移动到软件包中并在测试中移动更改导入语句。例如:

+---python2unittest
¦       a.py
¦       b.py
¦       __init__.py
+---test
¦   ¦   a.py
¦   ¦   b.py

您的测试用例test / a.py将如下所示:

import unittest

from python2unittest import a


class TestA(unittest.TestCase):

    def test_hello(self):
        self.assertEqual(a.hello(), None)

如何从命令行运行测试:

python -m unittest discover -s  -t test -p a.py  // Specific Test
python -m unittest discover -s  -t test -p *.py  // All tests

注意: - 如果您有测试文件的子目录,则需要在每个子目录中添加__init__.py文件,以便unittest可以发现所有测试文件。

可以找到有关测试发现的更多信息here

答案 3 :(得分:1)

我认为您在问题中显示的结构就像您之前使用的是某些IDE,例如Pycharm。

在Pycharm中,你绝对可以拥有那个项目结构。您可以在单个文件中运行unittest,只需右键单击它并点击Run 'unittest in a.py'即可。您可以通过右键单击目录并单击unittest在整个目录中运行Run 'unittest in test'

此外,您无需在__ini__.py中创建test,以使其成为可导入的模块。您也可以在测试文件中直接import your_lib,而无需担心导入路径问题。

所有这些都是通过IDE的魔力完成的,因此您无法仅使用python -m unittest ...直接实现此目的。如果可以,为什么Pycharm使用名为_jb_unittest_runner.py的帮助器?

但是你可以创建一个简单的bash脚本来实现类似的行为。

关键是:

  • 将项目根分配给变量,例如PROJECT_HOME
  • 将测试根分配给变量,例如TEST_HOME
  • PROJECT_HOME添加到PYTHONPATH,以便您可以直接在测试文件中导入项目。
  • 将您的工作目录更改为${TEST_HOME}
  • 使用python -m unittest ${PROJECT_HOME}/test/${file_name}运行 单个文件。
  • 使用python -m unittest discover -s ${PROJECT_HOME}/test -t ${PROJECT_HOME}/test运行整个目录。
  • 使用python -m unittest ${file_name}.${test_case}运行单个测试用例。

答案 4 :(得分:0)

您的python包myproj和test下似乎没有__init__.py文件。您无法在测试中导入模块。

请在test和myproj目录中添加空白__init__.py文件。如果这不能解决问题,请提供任何测试文件的代码。

您在test / b.py文件中使用了import b,但它应该来自myproj import b。因为如果您只使用导入b,它将导入当前文件(您已在其中编写测试,此文件没有任何bye()方法)。因此,您可以更改测试文件的文件名或使用下面的代码。

# test/b.py

import unittest
from myproj import b

class TestB(unittest.TestCase):
    def test_bye(self):
        self.assertEqual(b.bye(), None)

答案 5 :(得分:0)

我相当肯定你的问题是你运行的是Python 2.7,其中做一个普通的import a是模棱两可的,它意味着相对于当前模块或基本路径的那个。添加行 from __future__ import absolute_import到文件的顶部(它必须是第一个非评论行)。我建议您对所有文件执行此操作,但它应该足以在test / * .py文件上执行。

完成后,您应该能够运行 python -m unittest discover -s test -p '*.py'

答案 6 :(得分:0)

前段时间我有类似的要求(python 2.6)我最后通过基于文件路径动态加载模块来实现一个可运行测试的小可执行脚本(称之为run_tests,代码如下)。 / p>

必须进行的唯一更改是将源模块放入一个包中,这很有意义,因为您要从测试模块中导入这些源模块。

如果您确实希望将源模块放在项目的根目录中,那么也可以使用此解决方案,但是您必须将测试模块重命名为不同的模块(例如test_a.py )。如果你想要我,我可以调整当前的解决方案,使其成功。

您的项目结构需要如下所示:

.
├── project
│   ├── a.py
│   └── __init__.py
└── test
    ├── a.py
    └── run_tests

此处,project是包含源a.py的包,请记住,要使其成为包,project文件夹中需要一个空的__init__.py文件。< / p>

现在run_tests的内容:

#!/usr/bin/env python

import fnmatch
import imp
import os
import sys
import unittest

append_lib = lambda p: sys.path.append(p)
root_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
test_dir = os.path.join(root_dir, "test")

append_lib(root_dir) # packages to be tested are to be found there

def find_files():
  matches = []
  for root, dirnames, filenames in os.walk(test_dir):
    for filename in fnmatch.filter(filenames, "*.py"):
      matches.append(os.path.join(root, filename))
  return matches

def module_from_path(path):
  module = os.path.splitext(os.path.basename(path))[0]
  return imp.load_source(module, path)

def run_tests_in_paths(paths):
  suite = unittest.TestSuite()
  for p in paths:
    s = unittest.findTestCases(module_from_path(p))
    suite.addTests(s)
  if not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful():
    raise RuntimeError("some test cases failed")

if __name__ == "__main__":
  if len(sys.argv) > 1:
    paths = sys.argv[1:]
    print "> Running %s individual test file%s" % (len(paths), "s" if len(paths) > 1 else "")
    run_tests_in_paths(sys.argv[1:])
  else:
    print "> Running complete test suite"
    run_tests_in_paths(find_files())

我使用此project/a.py的内容测试了此设置:

class A(object):
  pass

对于test/a.py

import unittest

from project import a

class TestA(unittest.TestCase):
  def test_hello(self):
    self.assertNotEqual(a.A(), None)

您现在可以为单个文件运行run_tests脚本(不要忘记chmod +x):

./test/run_tests test/a.py

或者喜欢在test文件夹中运行任何测试模块:

./test/run_tests

注意:不要犹豫,改进/自定义run_tests脚本以更好地满足您的需求。

我希望这有帮助!

答案 7 :(得分:-1)

您最好以setuptools的方式打包您的应用,这样可以使常规的包文件布局更容易。

Anayway你应该尝试自动发现模式:

  • 放一个test/__init__.py文件。甚至是空的。
  • 使用unittest.TestCase方法将test/anything.py中继承test_xxx的类放入
  • cd root/of/project
  • python -m unittest discover -s test -p *.py

有关使用的选项,请参阅https://docs.python.org/2.7/library/unittest.html#test-discovery