如何为bin目录中的脚本编写Python单元测试

如何为bin目录中的脚本编写Python单元测试,python,unit-testing,python-unittest,Python,Unit Testing,Python Unittest,Pythonunittest模块似乎为项目假设了一个目录结构,其中有一个项目根目录,该目录下有源代码和测试 但是,我想在我的~/bin目录中编写Python脚本,并在另一个目录中对其进行测试(例如,~/dev/tests)。有没有一种方法可以让我使用命令行界面运行单元测试,而不必设置myPYTHONPATH环境变量并创建\uuuuu init\uuuuuuu.py文件等等 下面是一个简单的示例,演示了我想要的: ~/bin/candy: #!/usr/bin/env python def ca

Python
unittest
模块似乎为项目假设了一个目录结构,其中有一个项目根目录,该目录下有源代码和测试

但是,我想在我的
~/bin
目录中编写Python脚本,并在另一个目录中对其进行测试(例如,
~/dev/tests
)。有没有一种方法可以让我使用命令行界面运行单元测试,而不必设置my
PYTHONPATH
环境变量并创建
\uuuuu init\uuuuuuu.py
文件等等

下面是一个简单的示例,演示了我想要的:

~/bin/candy

#!/usr/bin/env python

def candy():
    return "candy"

if __name__ == '__main__':
    print candy()
~/dev/tests/test\u candy.py

#!/usr/bin/env python

import unittest
import candy

class CandyTestCase(unittest.TestCase):

    def testCandy(self):
        candyOutput = candy.candy()

        assert candyOutput == "candy"
我注意到,如果:

  • 这两个文件以py扩展名命名(
    candy.py
    test\u candy.py
  • 这两个文件位于同一目录中
  • 测试在测试目录中使用以下内容运行: $python-m单元测试
我可以使用
unittest
模块运行python来执行以下操作,而无需在我的环境中明确设置任何内容:

  • 我的测试文件没有py扩展名(只是
    ~/candy
  • 我不在乎
    test\u candy
    是否将py作为扩展
  • 我希望
    candy
    test\u candy.py
    不共享公共根目录(我的主目录除外)

如果简单调用
python-m unittest不可能做到这一点,那么实现这一点最简单的方法是什么?

我没有用unittest尝试过这一点,但我的快速修复方法是使用操作系统模块更改脚本中的工作目录。不过这对你应该有用

#!/usr/bin/env python

import unittest
import os
os.chdir("/usr/bin/candy")
import candy

class CandyTestCase(unittest.TestCase):

    def testCandy(self):
        candyOutput = candy.candy()

        assert candyOutput == "candy"

简而言之,不是。问题是你需要导入你要测试的模块,所以至少你必须更改你的PYTHONPATH。我的建议是,在执行测试时暂时更改它,如下所示:

import sys
sys.path.extend(['~/dir1','~/dir2','~/anotherdir'])
@Paritosh_Singh的解决方案很好

从长远来看,需要安装一个测试运行程序,如tox,并对其进行配置,使其“看到”您的模块。但我认为你不想那样做

这是candy可执行文件(无更改):

这是
~/dev/tests/test\u candy.py
(已更改):

什么改变了

  • 我们添加了
    imp.load\u source
    以导入
    ~/bin/candy
    (一个没有
    *.py
    扩展名的模块)

  • 我们增加了使用
    expanduser查找主目录的规定,即
    ~

  • 我们正在使用
    os.path.join
    来加入
    ~/bin/candy

现在,您可以使用
unittest
模块的
discover
选项运行测试

查看python-m unittest--help以了解更多详细信息

以下摘录

-s启动发现的目录('.'默认)

-p匹配测试文件的模式('test*.py'默认值)


自Python 3.3以来,
imp
包已被弃用,而
importlib
包取代了它。提供如何导入单个文件的详细信息

对于单元测试,这将是:

来自importlib.machine导入模块,SourceFileLoader
从importlib.util从加载程序导入规范,从规范导入模块
导入操作系统路径
导入类型
导入单元测试
def import_from_source(名称:str,文件路径:str)->types.ModuleType:
加载程序:SourceFileLoader=SourceFileLoader(名称、文件路径)
spec:ModuleSpec=spec\u from\u loader(loader.name,loader)
module:types.ModuleType=module\u from\u spec(spec)
loader.exec_模块(模块)
返回模块
脚本路径:str=os.path.abspath(
os.path.join(
os.path.dirname(os.path.abspath(_文件__))、“.”、“.”、“.bin”、“candy”,
)
)
candy:types.ModuleType=import\u from\u source(“candy”,脚本\u路径)
类CandyTestCase(unittest.TestCase):
def testCandy(自:“CandyTestCase”)->无:
self.assertEqual(candy.candy(),“candy”)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
unittest.main()
假设文件结构为:

base_directory/bin/candy
base_directory/dev/tests/test_candy.py

(注意:此单元测试假定来自测试的固定相对路径,而不是固定绝对路径,以便您可以将包移动到另一个目录,并且只要包中的文件不改变相对位置,测试就不会中断。)

为什么编写python文件时没有扩展名
.py
呢?python库中的模块应该有.py扩展名。另一方面,可执行脚本,无论是shell、perl、python还是其他什么,都不需要.ksh、.bash、.py等扩展,因为它们的用户不需要知道如何用什么语言实现它们来运行它们。我通常不使用扩展名来命名可执行文件,扩展名表示脚本的类型。对于临时测试来说,这样做很容易,只需使用
imp.load\u source(模块名称,'bin/EXECUTABLE\u FILE')
,我发现我遇到的问题是在执行去ILD时,它不会将不以.py结尾的文件复制到build dir,这样这些脚本就不会在build@PeterTurner你能再详细一点吗?您正在执行什么类型的构建?如果是
distutils
/
setuptools
build,那么通过
scripts
列表传递到
setup
函数的脚本将复制到
build/scripts-X.Y
,其中X.Y是解释器版本。@hoefling,哦,我没有“scripts”列表,我必须查一下,我正在用一些参数做一个去毛刺,一时记不起来了,我周一会查的
➜ cat ~/dev/tests/test_candy.py

#!/usr/bin/env python

import imp
import unittest

from os.path import expanduser, join

# use expanduser to locate its home dir and join bin and candy module paths
candy_module_path =  join(expanduser("~"), "bin", "candy")

# load the module without .py extension
candy = imp.load_source("candy", candy_module_path)


class CandyTestCase(unittest.TestCase):

    def testCandy(self):
        candyOutput = candy.candy()

        assert candyOutput == "candy"
➜ python -m unittest discover -s ~/bin/ -p 'test*' -v ~/dev/tests
testCandy (test_candy.CandyTestCase) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
base_directory/bin/candy
base_directory/dev/tests/test_candy.py