簡體   English   中英

使用unittest discover傳遞參數(對於argparse)

[英]Passing arguments (for argparse) with unittest discover

foo是一個具有深層目錄嵌套的Python項目,包括各個子目錄中的~30個unittest文件。 foosetup.py ,我添加了一個內部運行的自定義“test”命令

 python -m unittest discover foo '*test.py'

請注意,這使用了unittest的發現模式。


由於一些測試非常慢,我最近決定測試應該有“級別”。 這個問題的答案很好地解釋了如何讓unittestargparse相互配合。 所以現在,我可以運行一個單獨的 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')更改為其他內容很好,只要:

  1. 它允許為多文件單元測試生成單個報告。
  2. 它可以某種方式支持多個測試級別(使用問題中的技術,或使用其他一些不同的機制)。

使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM