简体   繁体   English

使用unittest.TestCase实例的`setUp`和`tearDown`的不同实现

[英]Use different implementations of `setUp` and `tearDown` of unittest.TestCase instances

I want to run a set of tests under different conditions and therefore share these tests between two different TestCase -derived classes. 我想在不同的条件下运行一组测试,因此在两个不同的TestCase派生类之间共享这些测试。 One creates its own standalone session and the other attaches to an existing session and executes the same tests in there. 一个创建自己的独立会话,另一个连接到现有会话并在其中执行相同的测试。

I guess I'm kind of abusing the unittest framework when testing an API with it but it doesn't feel like it's too far from its original purpose. 我猜想我在用它测试API时会滥用unittest框架,但感觉它与最初的目的并不太远。 Am I good so far? 到目前为止我还好吗?

I hacked a few things together and got it kind of running. 我一起砍了几件事,并使其运行起来。 But the way it's done, doesn't feel right and I'm afraid will cause problems sooner or later. 但是这样做的方式并没有感觉正确,恐怕迟早会引起问题。

These are the problems I have with my solution: 这些是我的解决方案所遇到的问题:

  • When simply running the thing with PyCharm without limiting the tests, it attempts to run not only the intended StandaloneSessionTests and ExistingSessionTests but also GroupOfTests which is only the collection and has no session, ie execution context. 当简单地使用PyCharm运行事物而不限制测试时,它不仅尝试运行预期的StandaloneSessionTestsExistingSessionTests ,而且尝试运行GroupOfTests ,后者只是集合,没有会话,即执行上下文。

  • I can make it not run GroupOfTests by not deriving that one from TestCase but then PyCharm complains that it doesn't know about the assert...() functions. 我可以通过不从TestCase派生一个GroupOfTests来使其运行,但是PyCharm抱怨它不知道assert...()函数。 Rightly so, because GroupOfTest only gets indirect access to these functions at runtime when a derived class also inherits from TestCase . GroupOfTest ,因为当派生类也继承自TestCase时, GroupOfTest仅在运行时间接访问这些函数。 Coming from a C++ background, this feels like black magic and I don't think I should be doing this. 来自C ++背景,这感觉就像是黑魔法,我认为我不应该这样做。

I tried passing the session creation classes to the constructor of GroupOfTests like this: __init__(self, session_class) . 我尝试将会话创建类传递给GroupOfTests的构造函数,例如: __init__(self, session_class) GroupOfTests __init__(self, session_class) But this causes problems when the unittest framework attempts to instantiate the tests: It doesn't know what to do with the additional __init__ parameter. 但是,这会导致问题时, unittest框架尝试实例测试:它不知道如何处理额外做__init__参数。

I learned about @classmethod , which seems to be a way to get around the "only one constructor" limitation of Python but I couldn't figure out a way to get it running. 我了解了@classmethod ,这似乎是解决Python“仅一个构造函数”限制的一种方法,但我想不出一种使它运行的方法。

I'm looking for a solution that lets me state something as straightforward as this: 我正在寻找一种解决方案,使我可以说出这样简单的内容:

suite = unittest.TestSuite()
suite.addTest(GroupOfTests(UseExistingSession))
suite.addTest(GroupOfTests(CreateStandaloneSession))
...

This is what I got so far: 这是我到目前为止所得到的:

#!/usr/bin/python3

import unittest


def existing_session():
    return "existingsession"


def create_session():
    return "123"


def close_session(session_id):
    print("close session %s" % session_id)
    return True


def do_thing(session_id):
    return len(session_id)


class GroupOfTests(unittest.TestCase):  # GroupOfTests gets executed, which makes no sense.
#class GroupOfTests:  # use of assertGreaterThan() causes pycharm warning
    session_id = None

    def test_stuff(self):
        the_thing = do_thing(self.session_id)
        self.assertGreater(the_thing, 2)

    # Original code contains many other tests, which must not be duplicated


class UseExistingSession(unittest.TestCase):
    session_id = None

    def setUp(self):
        self.session_id = existing_session()

    def tearDown(self):
        pass  # Nothing to do


class CreateStandaloneSession(unittest.TestCase):
    session_id = None

    def setUp(self):
        self.session_id = create_session()

    def tearDown(self):
        close_session(self.session_id)


# unittest framework runs inherited test_stuff()
class StandaloneSessionTests(CreateStandaloneSession, GroupOfTests):
    pass


# unittest framework runs inherited test_stuff()
class ExistingSessionTests(UseExistingSession, GroupOfTests):
    pass


def main():
    suite = unittest.TestSuite()
    suite.addTest(StandaloneSessionTests)
    suite.addTest(ExistingSessionTests)

    runner = unittest.TextTestRunner()
    runner.run(suite())


if __name__ == '__main__':
    main()

I'm not sure if using pytest is an option for you but if so, here is an example which might do what you want. 我不确定是否可以使用pytest ,但是如果可以,这是一个pytest您需要的示例。

import pytest


class Session:
    def __init__(self, session_id=None):
        self.id = session_id


existing_session = Session(999)
new_session = Session(111)


@pytest.fixture(params=[existing_session, new_session])
def session_fixture(request):
    return request.param


class TestGroup:
    def test_stuff(self, session_fixture):
        print('(1) Test with session: {}'.format(session_fixture.id))
        assert True

    def test_more_stuff(self, session_fixture):
        print('(2) Test with session: {}'.format(session_fixture.id))
        assert True

Output: 输出:

$ pytest -v -s hmm.py
======================================================= test session starts ========================================================
platform linux -- Python 3.6.4, pytest-3.4.1, py-1.5.2, pluggy-0.6.0 -- /home/lettuce/Dropbox/Python/Python_3/venv/bin/python
cachedir: .pytest_cache
rootdir: /home/lettuce/Dropbox/Python/Python_3, inifile:
collected 4 items                                                                                                                  

hmm.py::TestGroup::test_stuff[session_fixture0] (1) Test with session: 999
PASSED
hmm.py::TestGroup::test_stuff[session_fixture1] (1) Test with session: 111
PASSED
hmm.py::TestGroup::test_more_stuff[session_fixture0] (2) Test with session: 999
PASSED
hmm.py::TestGroup::test_more_stuff[session_fixture1] (2) Test with session: 111
PASSED

===================================================== 4 passed in 0.01 seconds =====================================================

If you are actually going to use pytest you will probably want follow the conventions for Python test discovery rather than using hmm.py as a filename though! 如果您实际上要使用pytest ,则可能需要遵循Python测试发现约定,而不要使用hmm.py作为文件名!

You can get pycharm to ignore these not existing function by creating abstract methods with raise NotImplementetError : 您可以通过使用raise NotImplementetError创建抽象方法来使pycharm忽略这些不存在的函数:

class GroupOfTests:
    session_id = None

    def test_stuff(self):
        the_thing = do_thing(self.session_id)
        self.assertGreater(the_thing, 2)

    def assertGreater(self, a, b): # Pycharm treats these like abstract methods from the ABC module
        raise NotImplementetError

This will let python believe this is an abstract class and will make pycharm raise errors if a subclass doesn't define these functions. 这会让python相信这是一个抽象类,如果子类未定义这些函数,则会使pycharm引发错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM