【问题标题】:Python unittest passing argumentsPython unittest 传递参数
【发布时间】:2012-07-07 23:41:54
【问题描述】:

在 Python 中,如何将参数从命令行传递给 unittest 函数?

这是目前为止的代码……我知道这是错误的。

class TestingClass(unittest.TestCase):

    def testEmails(self):
        assertEqual(email_from_argument, "my_email@example.com")


if __name__ == "__main__":
    unittest.main(argv=[sys.argv[1]])
    email_from_argument = sys.argv[1]

【问题讨论】:

标签: python unit-testing


【解决方案1】:

所以这里的医生说“你说那很痛?那就不要那样做!”可能是对的。但如果你真的想要,这里有一种将参数传递给单元测试测试的方法:

import sys
import unittest

class MyTest(unittest.TestCase):
    USERNAME = "jemima"
    PASSWORD = "password"

    def test_logins_or_something(self):
        print('username:', self.USERNAME)
        print('password:', self.PASSWORD)


if __name__ == "__main__":
    if len(sys.argv) > 1:
        MyTest.USERNAME = sys.argv.pop()
        MyTest.PASSWORD = sys.argv.pop()
    unittest.main()

这会让你运行:

python mytests.py myusername mypassword

您需要argv.pops,因此您的命令行参数不会与 unittest 自己的...

您可能想要研究的另一件事是使用环境变量:

import os
import unittest

class MyTest(unittest.TestCase):
    USERNAME = "jemima"
    PASSWORD = "password"

    def test_logins_or_something(self):
        print('username:', self.USERNAME)
        print('password:', self.PASSWORD)

if __name__ == "__main__":
    MyTest.USERNAME = os.environ.get('TEST_USERNAME', MyTest.USERNAME)
    MyTest.PASSWORD = os.environ.get('TEST_PASSWORD', MyTest.PASSWORD)
    unittest.main()

这会让你运行:

TEST_USERNAME=ausername TEST_PASSWORD=apassword python mytests.py

它的优点是你不会弄乱 unittest 自己的参数解析。缺点是它不会像在 Windows 上那样工作......

【讨论】:

  • 看起来 pytest 页面已移动。现在在docs.pytest.org
  • 仅供参考,我过去做过类似的事情:使用“--”将我的 args 与 unittest 可能需要的 args 分开;然后我可以将“--”之后的参数列表传递给我自己的 arg 解析器,并将它们从 sys.argv 中删除,或者使用 unittest.main(argv=smaller_list) 显式传递 args unittest 可能想要的
  • sys.argv.pop() 方法似乎不适用于python3。我很想知道如何在python3 中做到这一点。
  • @Yeow_Meng,您可能需要更具体一些。我将它与 python-3.6.5 一起使用,它可以工作。
  • 如果您想针对不同环境(如不同阶段等)运行相同的测试,非常方便。可能的环境变量是最干净的。
【解决方案2】:

对于那些确实想这样做的人的另一种方法,尽管你不应该有正确的评论:

import unittest

class MyTest(unittest.TestCase):

    def __init__(self, testName, extraArg):
        super(MyTest, self).__init__(testName)  # calling the super class init varies for different python versions.  This works for 2.7
        self.myExtraArg = extraArg

    def test_something(self):
        print(self.myExtraArg)

# call your test
suite = unittest.TestSuite()
suite.addTest(MyTest('test_something', extraArg))
unittest.TextTestRunner(verbosity=2).run(suite)

【讨论】:

  • 好例子!另一个在agiletesting.blogspot.com/2005/01/…。但是小心传递的论点不会与单元测试的混乱..
  • 谢谢。这有助于弄清楚如何使单元测试适用于我的特定案例。
  • 我可以批量做吗?如果 MyTest 类中有多个测试
  • 请问您为什么选择不使用 unitest.main() 而是使用 TestSuite/TextTestRunner?
【解决方案3】:

即使测试大师说我们不应该这样做:我愿意。在某些情况下,使用参数来推动测试朝着正确的方向发展是很有意义的,例如:

  • 我现在应该使用十几个相同的 USB 卡中的哪一个?
  • 我现在应该使用哪个服务器进行此测试?
  • 我应该使用哪个 XXX?

对我来说,环境变量的使用对于这个 puprose 来说已经足够好了,因为您不必编写专门的代码来传递您的参数;它由 Python 支持。它干净简单。

当然,我并不是在提倡完全可参数化的测试。但我们必须务实,正如我所说,在某些情况下,您需要一两个参数。我们不应该滥用它:)

import os
import unittest


class MyTest(unittest.TestCase):
    def setUp(self):
        self.var1 = os.environ["VAR1"]
        self.var2 = os.environ["VAR2"]

    def test_01(self):
        print("var1: {}, var2: {}".format(self.var1, self.var2))

然后从命令行(在 Linux 上测试)

$ export VAR1=1
$ export VAR2=2
$ python -m unittest MyTest
var1: 1, var2: 2
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

【讨论】:

    【解决方案4】:

    如果你想使用steffens21's approachunittest.TestLoader,你可以修改原来的测试加载器(见unittest.py):

    import unittest
    from unittest import suite
    
    class TestLoaderWithKwargs(unittest.TestLoader):
        """A test loader which allows to parse keyword arguments to the
           test case class."""
        def loadTestsFromTestCase(self, testCaseClass, **kwargs):
            """Return a suite of all tests cases contained in 
               testCaseClass."""
            if issubclass(testCaseClass, suite.TestSuite):
                raise TypeError("Test cases should not be derived from "\
                                "TestSuite. Maybe you meant to derive from"\ 
                                " TestCase?")
            testCaseNames = self.getTestCaseNames(testCaseClass)
            if not testCaseNames and hasattr(testCaseClass, 'runTest'):
                testCaseNames = ['runTest']
    
            # Modification here: parse keyword arguments to testCaseClass.
            test_cases = []
            for test_case_name in testCaseNames:
                test_cases.append(testCaseClass(test_case_name, **kwargs))
            loaded_suite = self.suiteClass(test_cases)
    
            return loaded_suite 
    
    # call your test
    loader = TestLoaderWithKwargs()
    suite = loader.loadTestsFromTestCase(MyTest, extraArg=extraArg)
    unittest.TextTestRunner(verbosity=2).run(suite)
    

    【讨论】:

    • self.getTestCaseNames 方法从何而来?
    • 属于TestLoaderWithKwargs继承的unittest.TestLoader类。
    【解决方案5】:

    我也有同样的问题。我的解决方案是在您使用 argparse 或其他方式处理解析参数后,从 sys.argv 中删除参数:

    sys.argv = sys.argv[:1]  
    

    如果需要,可以从 main.parseArgs() 中过滤单元测试参数。

    【讨论】:

      【解决方案6】:

      这是我的解决方案:

      # your test class
      class TestingClass(unittest.TestCase):
      
          # This will only run once for all the tests within this class
          @classmethod
          def setUpClass(cls) -> None:
             if len(sys.argv) > 1:
                cls.email = sys.argv[1]
      
          def testEmails(self):
              assertEqual(self.email, "my_email@example.com")
      
      
      if __name__ == "__main__":
          unittest.main()
      

      你可以有一个类似这样的runner.py 文件:

      # your runner.py
      loader = unittest.TestLoader()
      tests = loader.discover('.') # note that this will find all your tests, you can also provide the name of the package e.g. `loader.discover('tests')
      runner = unittest.TextTestRunner(verbose=3)
      result = runner.run(tests
      

      使用上面的代码,您应该使用runner.py my_email@example.com 运行您的测试。

      【讨论】:

        【解决方案7】:

        Unit testing 用于测试非常基本的功能(应用程序的最低级别功能),以确保您的应用程序构建块正常工作。这究竟是什么意思可能没有正式的定义,但您应该考虑对更大的功能进行其他类型的测试——请参阅Integration testing。单元测试框架可能不适合此目的。

        【讨论】:

        • 是的,但是 unittest 框架本身可以用于(并且在许多情况下使用)来开发和集成测试框架,这通常需要复杂的设置和拆卸。在这种情况下,如果可能的话,最好有一个带有测试函数和参数的基类,以及一个带有参数定义的继承类,或者使用基于环境的配置脚本。
        • 同意 Umar 的观点,unittest 框架对于其最初目的之外的事情非常有用,如果你小心的话,它非常方便。尤其是因为这样您就可以与所有其他工具集成,例如它可以输出到的东西。
        猜你喜欢
        • 2023-03-28
        • 2016-10-07
        • 2016-05-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-21
        • 2019-12-10
        • 1970-01-01
        相关资源
        最近更新 更多