Programing

기본 및 하위 클래스를 사용한 Python 단위 테스트

crosscheck 2020. 6. 27. 11:13
반응형

기본 및 하위 클래스를 사용한 Python 단위 테스트


현재 공통 테스트 세트를 공유하는 몇 가지 단위 테스트가 있습니다. 예를 들면 다음과 같습니다.

import unittest

class BaseTest(unittest.TestCase):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

위의 결과는 다음과 같습니다.

Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s

OK

첫 번째 testCommon가 호출되지 않도록 위의 내용을 다시 쓰는 방법이 있습니까?

편집 : 위의 5 가지 테스트를 실행하는 대신 SubTest1에서 2 개, SubTest2에서 2 개를 4 개만 실행하려고합니다. 파이썬 unittest는 독자적인 BaseTest를 자체적으로 실행하고있는 것으로 보이며 그 발생을 막기위한 메커니즘이 필요합니다.


다중 상속을 사용하므로 일반적인 테스트를 수행하는 클래스 자체는 TestCase에서 상속되지 않습니다.

import unittest

class CommonTests(object):
    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(unittest.TestCase, CommonTests):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(unittest.TestCase, CommonTests):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

다중 상속을 사용하지 마십시오 . 나중에 물릴 것 입니다.

대신 기본 클래스를 별도의 모듈로 옮기거나 빈 클래스로 래핑 할 수 있습니다.

import unittest

class BaseTestCases:

    class BaseTest(unittest.TestCase):

        def testCommon(self):
            print 'Calling BaseTest:testCommon'
            value = 5
            self.assertEquals(value, 5)


class SubTest1(BaseTestCases.BaseTest):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTestCases.BaseTest):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

출력 :

Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

단일 명령으로이 문제를 해결할 수 있습니다.

del(BaseTest)

따라서 코드는 다음과 같습니다.

import unittest

class BaseTest(unittest.TestCase):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

del(BaseTest)

if __name__ == '__main__':
    unittest.main()

Matthew Marshall의 대답은 훌륭하지만 각 테스트 사례에서 오류가 발생하기 쉬운 두 클래스에서 상속해야합니다. 대신, 나는 이것을 사용합니다 (python> = 2.7) :

class BaseTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        if cls is BaseTest:
            raise unittest.SkipTest("Skip BaseTest tests, it's a base class")
        super(BaseTest, cls).setUpClass()

당신은 무엇을 달성하려고합니까? 당신이 일반적인 테스트 코드 (주장, 템플릿 테스트 등)이 있다면,로 시작하지 않는 방법에 배치 test그래서 unittest그들을로드되지 않습니다.

import unittest

class CommonTests(unittest.TestCase):
      def common_assertion(self, foo, bar, baz):
          # whatever common code
          self.assertEqual(foo(bar), baz)

class BaseTest(CommonTests):

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(CommonTests):

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)

class SubTest2(CommonTests):

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

매튜의 대답은 제가 2.5 살 때부터 사용했던 것입니다. 그러나 2.7부터 건너 뛰려는 모든 테스트 메소드에서 @ unittest.skip () 데코레이터를 사용할 수 있습니다.

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

기본 유형을 확인하려면 자체 건너 뛰기 데코레이터를 구현해야합니다. 이 기능을 전에 사용하지는 않았지만 머리 꼭대기에서 BaseTest를 마커 유형으로 사용하여 건너 뛰기를 조정할 수 있습니다 .

def skipBaseTest(obj):
    if type(obj) is BaseTest:
        return unittest.skip("BaseTest tests skipped")
    return lambda func: func

내가 이것을 해결하려고 생각한 방법은 기본 클래스가 사용되는 경우 테스트 메소드를 숨기는 것입니다. 이렇게하면 테스트를 건너 뛰지 않으므로 많은 테스트보고 도구에서 테스트 결과가 노란색 대신 녹색이 될 수 있습니다.

PyCharm과 같은 ide는 mixin 메소드와 비교할 때 단위 테스트 메소드가 기본 클래스에서 누락되었다고 불평하지 않습니다.

기본 클래스가이 클래스에서 상속되면 setUpClassand tearDownClass메소드 를 대체해야 합니다.

class BaseTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls._test_methods = []
        if cls is BaseTest:
            for name in dir(cls):
                if name.startswith('test') and callable(getattr(cls, name)):
                    cls._test_methods.append((name, getattr(cls, name)))
                    setattr(cls, name, lambda self: None)

    @classmethod
    def tearDownClass(cls):
        if cls is BaseTest:
            for name, method in cls._test_methods:
                setattr(cls, name, method)
            cls._test_methods = []

다른 옵션은 실행하지 않는 것입니다

unittest.main()

그 대신에 당신은 사용할 수 있습니다

suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)

따라서 클래스에서만 테스트를 실행하십시오. TestClass


__test_ = FalseBaseTest 클래스에 추가 할 수 있지만 추가하면 __test__ = True테스트를 실행할 수 있도록 파생 클래스를 추가해야합니다 .

import unittest

class BaseTest(unittest.TestCase):
    __test__ = False

    def testCommon(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)

class SubTest1(BaseTest):
    __test__ = True

    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):
    __test__ = True

    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

if __name__ == '__main__':
    unittest.main()

@Vladim P. ( https://stackoverflow.com/a/25695512/2451329 ) 와 거의 동일 하지만 약간 수정되었습니다.

import unittest2


from some_module import func1, func2


def make_base_class(func):

    class Base(unittest2.TestCase):

        def test_common1(self):
            print("in test_common1")
            self.assertTrue(func())

        def test_common2(self):
            print("in test_common1")
            self.assertFalse(func(42))

    return Base



class A(make_base_class(func1)):
    pass


class B(make_base_class(func2)):

    def test_func2_with_no_arg_return_bar(self):
        self.assertEqual("bar", func2())

그리고 우리는 간다.


testCommon 메소드의 이름을 다른 것으로 바꾸십시오. Unittest는 (보통) 'test'가없는 것을 건너 뜁니다.

빠르고 간단한

  import unittest

  class BaseTest(unittest.TestCase):

   def methodCommon(self):
       print 'Calling BaseTest:testCommon'
       value = 5
       self.assertEquals(value, 5)

  class SubTest1(BaseTest):

      def testSub1(self):
          print 'Calling SubTest1:testSub1'
          sub = 3
          self.assertEquals(sub, 3)


  class SubTest2(BaseTest):

      def testSub2(self):
          print 'Calling SubTest2:testSub2'
          sub = 4
          self.assertEquals(sub, 4)

  if __name__ == '__main__':
      unittest.main()`

그래서 이것은 일종의 오래된 스레드이지만 오늘이 문제를 겪고 내 자신의 해킹을 생각했습니다. 기본 클래스를 통해 액세스 할 때 None 함수 값을 만드는 데코레이터를 사용합니다. 기본 클래스에 테스트가 없으면 실행되지 않으므로 설정 및 설정 클래스에 대해 걱정할 필요가 없습니다.

import types
import unittest


class FunctionValueOverride(object):
    def __init__(self, cls, default, override=None):
        self.cls = cls
        self.default = default
        self.override = override

    def __get__(self, obj, klass):
        if klass == self.cls:
            return self.override
        else:
            if obj:
                return types.MethodType(self.default, obj)
            else:
                return self.default


def fixture(cls):
    for t in vars(cls):
        if not callable(getattr(cls, t)) or t[:4] != "test":
            continue
        setattr(cls, t, FunctionValueOverride(cls, getattr(cls, t)))
    return cls


@fixture
class BaseTest(unittest.TestCase):
    def testCommon(self):
        print('Calling BaseTest:testCommon')
        value = 5
        self.assertEqual(value, 5)


class SubTest1(BaseTest):
    def testSub1(self):
        print('Calling SubTest1:testSub1')
        sub = 3
        self.assertEqual(sub, 3)


class SubTest2(BaseTest):

    def testSub2(self):
        print('Calling SubTest2:testSub2')
        sub = 4
        self.assertEqual(sub, 4)

if __name__ == '__main__':
    unittest.main()

As of Python 3.2, you can add a test_loader function to a module to control which tests (if any) are found by the test discovery mechanism.

For example, the following will only load the original poster's SubTest1 and SubTest2 Test Cases, ignoring Base:

def load_tests(loader, standard_tests, pattern):
    suite = TestSuite()
    suite.addTests([SubTest1, SubTest2])
    return suite

It ought to be possible to iterate over standard_tests (a TestSuite containing the tests the default loader found) and copy all but Base to suite instead, but the nested nature of TestSuite.__iter__ makes that a lot more complicated.


Change the BaseTest method name to setUp:

class BaseTest(unittest.TestCase):
    def setUp(self):
        print 'Calling BaseTest:testCommon'
        value = 5
        self.assertEquals(value, 5)


class SubTest1(BaseTest):
    def testSub1(self):
        print 'Calling SubTest1:testSub1'
        sub = 3
        self.assertEquals(sub, 3)


class SubTest2(BaseTest):
    def testSub2(self):
        print 'Calling SubTest2:testSub2'
        sub = 4
        self.assertEquals(sub, 4)

Output:

Ran 2 tests in 0.000s

Calling BaseTest:testCommon Calling
SubTest1:testSub1 Calling
BaseTest:testCommon Calling
SubTest2:testSub2

From the documentation:

TestCase.setUp()
Method called to prepare the test fixture. This is called immediately before calling the test method; any exception raised by this method will be considered an error rather than a test failure. The default implementation does nothing.

참고URL : https://stackoverflow.com/questions/1323455/python-unit-test-with-base-and-sub-class

반응형