[英]Passing arguments (for argparse) with unittest discover
foo
是一個具有深層目錄嵌套的Python項目,包括各個子目錄中的~30個unittest
文件。 在foo
的setup.py
,我添加了一個內部運行的自定義“test”命令
python -m unittest discover foo '*test.py'
請注意,這使用了unittest
的發現模式。
由於一些測試非常慢,我最近決定測試應該有“級別”。 這個問題的答案很好地解釋了如何讓unittest
和argparse
相互配合。 所以現在,我可以運行一個單獨的 unittest文件,比如說foo/bar/_bar_test.py
python foo/bar/_bar_test.py --level=3
並且只運行3級測試。
問題是我無法弄清楚如何使用發現傳遞自定義標志(在本例中為“--level = 3”。我嘗試的一切都失敗了,例如:
$ python -m unittest discover --level=3 foo '*test.py'
Usage: python -m unittest discover [options]
python -m unittest discover: error: no such option: --level
$ python -m --level=3 unittest discover foo '*test.py'
/usr/bin/python: No module named --level=3
如何將--level=3
傳遞給單個單元測試? 如果可能的話,我想避免將不同級別的測試划分為不同的文件。
賞金編輯
pre-bounty(精細)解決方案建議使用系統環境變量。 這還不錯,但我正在尋找更清潔的東西。
將多文件測試運行器(即python -m unittest discover foo'* test.py')更改為其他內容很好,只要:
使用discover時無法傳遞參數。 DiscoveringTestLoader
類,刪除所有不匹配的文件(使用'* test.py --level = 3'消除)並僅將文件名傳遞給unittest.TextTestRunner
到目前為止,可能只有使用環境變量的選項
LEVEL=3 python -m unittest discoverfoo '*test.py'
您遇到的問題是unittest參數解析器根本不理解這種語法。 因此,您必須在調用unittest之前刪除參數。
一個簡單的方法是創建一個包裝器模塊(比如my_unittest.py),它查找你的額外參數,從sys.argv中剝離它們,然后調用unittest中的主條目。
現在好了......這個包裝器的代碼與你已經用於單個文件的代碼基本相同! 您只需將其放入單獨的文件中即可。
編輯 :根據要求添加以下示例代碼...
首先,運行UTs的新文件(my_unittest.py):
import sys
import unittest
from parser import wrapper
if __name__ == '__main__':
wrapper.parse_args()
unittest.main(module=None, argv=sys.argv)
現在是parser.py,它必須位於一個單獨的文件中,以避免在__main__
模塊中進行全局引用:
import sys
import argparse
import unittest
class UnitTestParser(object):
def __init__(self):
self.args = None
def parse_args(self):
# Parse optional extra arguments
parser = argparse.ArgumentParser()
parser.add_argument('--level', type=int, default=0)
ns, args = parser.parse_known_args()
self.args = vars(ns)
# Now set the sys.argv to the unittest_args (leaving sys.argv[0] alone)
sys.argv[1:] = args
wrapper = UnitTestParser()
最后一個示例測試用例(project_test.py)來測試參數是否正確解析:
import unittest
from parser import wrapper
class TestMyProject(unittest.TestCase):
def test_len(self):
self.assertEqual(len(wrapper.args), 1)
def test_level3(self):
self.assertEqual(wrapper.args['level'], 3)
現在證明:
$ python -m my_unittest discover --level 3 . '*test.py'
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
這不會使用unittest discover傳遞args,但它會完成您要執行的操作。
這是leveltest.py
。 把它放在模塊搜索路徑中的某個位置(可能是當前目錄或站點包):
import argparse
import sys
import unittest
# this part copied from unittest.__main__.py
if sys.argv[0].endswith("__main__.py"):
import os.path
# We change sys.argv[0] to make help message more useful
# use executable without path, unquoted
# (it's just a hint anyway)
# (if you have spaces in your executable you get what you deserve!)
executable = os.path.basename(sys.executable)
sys.argv[0] = executable + " -m leveltest"
del os
def _id(obj):
return obj
# decorator that assigns test levels to test cases (classes and methods)
def level(testlevel):
if unittest.level < testlevel:
return unittest.skip("test level too low.")
return _id
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--level', type=int, default=3)
ns, args = parser.parse_known_args(namespace=unittest)
return ns, sys.argv[:1] + args
if __name__ == "__main__":
ns, remaining_args = parse_args()
# this invokes unittest when leveltest invoked with -m flag like:
# python -m leveltest --level=2 discover --verbose
unittest.main(module=None, argv=remaining_args)
以下是在testproject.py文件示例中使用它的方法:
import unittest
import leveltest
# This is needed before any uses of the @leveltest.level() decorator
# to parse the "--level" command argument and set the test level when
# this test file is run directly with -m
if __name__ == "__main__":
ns, remaining_args = leveltest.parse_args()
@leveltest.level(2)
class TestStringMethods(unittest.TestCase):
@leveltest.level(5)
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
@leveltest.level(3)
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
@leveltest.level(4)
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
# this invokes unittest when this file is executed with -m
unittest.main(argv=remaining_args)
然后,您可以通過直接運行testproject.py來運行測試,例如:
~roottwo\projects> python testproject.py --level 2 -v
test_isupper (__main__.TestStringMethods) ... skipped 'test level too low.'
test_split (__main__.TestStringMethods) ... skipped 'test level too low.'
test_upper (__main__.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK (skipped=3)
~roottwo\projects> python testproject.py --level 3 -v
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... skipped 'test level too low.'
test_upper (__main__.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK (skipped=2)
~roottwo\projects> python testproject.py --level 4 -v
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK (skipped=1)
~roottwo\projects> python testproject.py --level 5 -v
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
通過使用像這樣的unittest發現:
~roottwo\projects> python -m leveltest --level 2 -v
test_isupper (testproject.TestStringMethods) ... skipped 'test level too low.'
test_split (testproject.TestStringMethods) ... skipped 'test level too low.'
test_upper (testproject.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.003s
OK (skipped=3)
~roottwo\projects> python -m leveltest --level 3 discover -v
test_isupper (testproject.TestStringMethods) ... ok
test_split (testproject.TestStringMethods) ... skipped 'test level too low.'
test_upper (testproject.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK (skipped=2)
~roottwo\projects> python -m leveltest --level 4 -v
test_isupper (testproject.TestStringMethods) ... ok
test_split (testproject.TestStringMethods) ... ok
test_upper (testproject.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK (skipped=1)
~roottwo\projects> python -m leveltest discover --level 5 -v
test_isupper (testproject.TestStringMethods) ... ok
test_split (testproject.TestStringMethods) ... ok
test_upper (testproject.TestStringMethods) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
或者通過指定要運行的測試用例,例如:
~roottwo\projects>python -m leveltest --level 3 testproject -v
test_isupper (testproject.TestStringMethods) ... ok
test_split (testproject.TestStringMethods) ... skipped 'test level too low.'
test_upper (testproject.TestStringMethods) ... skipped 'test level too low.'
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK (skipped=2)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.