source

Python에서 장치 테스트의 데이터 출력

manysource 2023. 7. 24. 22:37

Python에서 장치 테스트의 데이터 출력

유닛 테스트를 Python(유닛 테스트 모듈 사용)으로 작성하는 경우, 실패한 테스트에서 데이터를 출력할 수 있습니까? 그러면 오류의 원인을 파악하는 데 도움이 될 수 있습니다.

일부 정보를 전달할 수 있는 사용자 지정 메시지를 만들 수 있는 기능은 알고 있지만 때로는 문자열로 쉽게 표현할 수 없는 복잡한 데이터를 처리할 수도 있습니다.

예를 들어, Foo 클래스가 있고 검정 데이터라는 목록의 데이터를 사용하여 방법 표시줄을 검정하고 있다고 가정합니다.

class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1)
            self.assertEqual(f.bar(t2), 2)

검정이 실패한 경우 t1, t2 및/또는 f를 출력하여 이 특정 데이터가 실패한 이유를 확인할 수 있습니다.즉, 테스트가 실행된 후 다른 변수와 마찬가지로 변수에 액세스할 수 있습니다.

이를 위해 로깅 모듈을 사용합니다.

예:

import logging
class SomeTest( unittest.TestCase ):
    def testSomething( self ):
        log= logging.getLogger( "SomeTest.testSomething" )
        log.debug( "this= %r", self.this )
        log.debug( "that= %r", self.that )
        self.assertEqual( 3.14, pi )

if __name__ == "__main__":
    logging.basicConfig( stream=sys.stderr )
    logging.getLogger( "SomeTest.testSomething" ).setLevel( logging.DEBUG )
    unittest.main()

따라서 실패한 것으로 알고 추가 디버깅 정보가 필요한 특정 테스트에 대해 디버깅을 켤 수 있습니다.

그러나 제가 선호하는 방법은 디버깅에 많은 시간을 할애하는 것이 아니라 문제를 노출하기 위해 더 세밀한 테스트를 작성하는 것입니다.

Python 2.7에서는 추가 매개 변수를 사용할 수 있습니다.msg다음과 같은 오류 메시지에 정보를 추가합니다.

self.assertEqual(f.bar(t2), 2, msg='{0}, {1}'.format(t1, t2))

공식 문서는 여기 있습니다.

간단한 인쇄문이나 다른 표준 출력에 쓰는 방법을 사용할 수 있습니다.테스트의 모든 위치에서 Python 디버거를 실행할 수도 있습니다.

만약 여러분이 테스트를 실행하기 위해 코를 사용한다면 (제가 추천하는), 그것은 각 테스트에 대한 표준 출력을 수집하고 테스트가 실패한 경우에만 보여줄 것이기 때문에, 여러분은 테스트가 통과했을 때 혼란스러운 출력을 가지고 살 필요가 없습니다.

nose에는 asserts에 언급된 변수를 자동으로 표시하거나 실패한 테스트에서 디버거를 호출하는 스위치도 있습니다.예를들면,-s(--nocapture)는 표준 출력의 캡처를 방지합니다.

이것은 당신이 찾고 있는 것이 아니라고 생각합니다.실패하지 않는 변수 값을 표시하는 방법은 없지만 원하는 방식으로 결과를 출력하는 데 도움이 될 수 있습니다.

TestRunner.run()에서 반환한 TestResult 개체를 결과 분석 및 처리에 사용할 수 있습니다.특히 TestResult.errors 및 TestResult.failures

테스트 결과 개체 정보:

http://docs.python.org/library/unittest.html#id3

그리고 올바른 방향을 알려줄 몇 가지 코드가 있습니다.

>>> import random
>>> import unittest
>>>
>>> class TestSequenceFunctions(unittest.TestCase):
...     def setUp(self):
...         self.seq = range(5)
...     def testshuffle(self):
...         # make sure the shuffled sequence does not lose any elements
...         random.shuffle(self.seq)
...         self.seq.sort()
...         self.assertEqual(self.seq, range(10))
...     def testchoice(self):
...         element = random.choice(self.seq)
...         error_test = 1/0
...         self.assert_(element in self.seq)
...     def testsample(self):
...         self.assertRaises(ValueError, random.sample, self.seq, 20)
...         for element in random.sample(self.seq, 5):
...             self.assert_(element in self.seq)
...
>>> suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
>>> testResult = unittest.TextTestRunner(verbosity=2).run(suite)
testchoice (__main__.TestSequenceFunctions) ... ERROR
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... FAIL

======================================================================
ERROR: testchoice (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 11, in testchoice
ZeroDivisionError: integer division or modulo by zero

======================================================================
FAIL: testshuffle (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 8, in testshuffle
AssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

----------------------------------------------------------------------
Ran 3 tests in 0.031s

FAILED (failures=1, errors=1)
>>>
>>> testResult.errors
[(<__main__.TestSequenceFunctions testMethod=testchoice>, 'Traceback (most recent call last):\n  File "<stdin>"
, line 11, in testchoice\nZeroDivisionError: integer division or modulo by zero\n')]
>>>
>>> testResult.failures
[(<__main__.TestSequenceFunctions testMethod=testshuffle>, 'Traceback (most recent call last):\n  File "<stdin>
", line 8, in testshuffle\nAssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n')]
>>>

제가 사용하는 방법은 정말 간단합니다.경고로 기록하면 실제로 표시됩니다.

import logging

class TestBar(unittest.TestCase):
    def runTest(self):

       #this line is important
       logging.basicConfig()
       log = logging.getLogger("LOG")

       for t1, t2 in testdata:
         f = Foo(t1)
         self.assertEqual(f.bar(t2), 2)
         log.warning(t1)

에는 이런경는에를 합니다.log.debug()내 애플리케이션에 메시지가 몇 개 있습니다.은 " " " "이기 때문에 " " 입니다.WARNING이러한 메시지는 정상적인 실행에는 표시되지 않습니다.

을 " " " " " " " " " " " " 으로 합니다.DEBUG실행 중에 이러한 메시지가 표시되도록 합니다.

import logging

log.debug("Some messages to be shown just when debugging or unit testing")

단위 테스트에서:

# Set log level
loglevel = logging.DEBUG
logging.basicConfig(level=loglevel)



전체 예를 참조하십시오.

는 이은입니다.daikiri.py이름과 가격으로 다이키리를 구현하는 기본 클래스.방법이 있습니다.make_discount()주어진 할인을 적용한 후 특정 다이키리의 가격을 반환하는 것:

import logging

log = logging.getLogger(__name__)

class Daikiri(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def make_discount(self, percentage):
        log.debug("Deducting discount...")  # I want to see this message
        return self.price * percentage

그런 다음, 단위 테스트를 만듭니다.test_daikiri.py사용 여부를 확인합니다.

import unittest
import logging
from .daikiri import Daikiri


class TestDaikiri(unittest.TestCase):
    def setUp(self):
        # Changing log level to DEBUG
        loglevel = logging.DEBUG
        logging.basicConfig(level=loglevel)

        self.mydaikiri = Daikiri("cuban", 25)

    def test_drop_price(self):
        new_price = self.mydaikiri.make_discount(0)
        self.assertEqual(new_price, 0)

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

그래서 내가 그것을 실행하면 나는log.debug메시지:

$ python -m test_daikiri
DEBUG:daikiri:Deducting discount...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

다른 옵션 - 테스트가 실패한 디버거를 시작합니다.

Testoob을 사용하여 테스트를 실행해 보십시오(변경 없이 장치 테스트 스위트를 실행합니다). 테스트가 실패할 경우 '--debug' 명령줄 스위치를 사용하여 디버거를 열 수 있습니다.

Windows의 터미널 세션은 다음과 같습니다.

C:\work> testoob tests.py --debug
F
Debugging for failure in test: test_foo (tests.MyTests.test_foo)
> c:\python25\lib\unittest.py(334)failUnlessEqual()
-> (msg or '%r != %r' % (first, second))
(Pdb) up
> c:\work\tests.py(6)test_foo()
-> self.assertEqual(x, y)
(Pdb) l
  1     from unittest import TestCase
  2     class MyTests(TestCase):
  3       def test_foo(self):
  4         x = 1
  5         y = 2
  6  ->     self.assertEqual(x, y)
[EOF]
(Pdb)

제가 이것을 너무 많이 생각한 것 같습니다.제가 생각해낸 한 가지 방법은 진단 데이터를 축적하는 글로벌 변수를 갖는 것입니다.

이와 같은 것:

log1 = dict()
class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1)
            if f.bar(t2) != 2:
                log1("TestBar.runTest") = (f, t1, t2)
                self.fail("f.bar(t2) != 2")

로깅 사용:

import unittest
import logging
import inspect
import os

logging_level = logging.INFO

try:
    log_file = os.environ["LOG_FILE"]
except KeyError:
    log_file = None

def logger(stack=None):
    if not hasattr(logger, "initialized"):
        logging.basicConfig(filename=log_file, level=logging_level)
        logger.initialized = True
    if not stack:
        stack = inspect.stack()
    name = stack[1][3]
    try:
        name = stack[1][0].f_locals["self"].__class__.__name__ + "." + name
    except KeyError:
        pass
    return logging.getLogger(name)

def todo(msg):
    logger(inspect.stack()).warning("TODO: {}".format(msg))

def get_pi():
    logger().info("sorry, I know only three digits")
    return 3.14

class Test(unittest.TestCase):

    def testName(self):
        todo("use a better get_pi")
        pi = get_pi()
        logger().info("pi = {}".format(pi))
        todo("check more digits in pi")
        self.assertAlmostEqual(pi, 3.14)
        logger().debug("end of this test")
        pass

용도:

# LOG_FILE=/tmp/log python3 -m unittest LoggerDemo
.
----------------------------------------------------------------------
Ran 1 test in 0.047s

OK
# cat /tmp/log
WARNING:Test.testName:TODO: use a better get_pi
INFO:get_pi:sorry, I know only three digits
INFO:Test.testName:pi = 3.14
WARNING:Test.testName:TODO: check more digits in pi

하지 않은 LOG_FILE은 로깅대위치로 이동합니다.stderr.

사용할 수 있습니다.logging모듈을 사용합니다.

따라서 장치 테스트 코드에서 다음을 사용합니다.

import logging as log

def test_foo(self):
    log.debug("Some debug message.")
    log.info("Some info message.")
    log.warning("Some warning message.")
    log.error("Some error message.")

및는 "" " " " " " " 로 됩니다./dev/stderr콘솔에서 볼 수 있어야 합니다.

로그(포맷 등)를 사용자 지정하려면 다음 샘플을 사용해 보십시오.

# Set-up logger
if args.verbose or args.debug:
    logging.basicConfig( stream=sys.stdout )
    root = logging.getLogger()
    root.setLevel(logging.INFO if args.verbose else logging.DEBUG)
    ch = logging.StreamHandler(sys.stdout)
    ch.setLevel(logging.INFO if args.verbose else logging.DEBUG)
    ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
    root.addHandler(ch)
else:
    logging.basicConfig(stream=sys.stderr)

inspect.dll을 사용하면 예외가 발생한 후 로컬 변수를 가져올 수 있습니다.그런 다음 다음 다음과 같은 장식기로 단위 검정을 래핑하여 사후 검사에 사용할 로컬 변수를 저장할 수 있습니다.

import random
import unittest
import inspect


def store_result(f):
    """
    Store the results of a test
    On success, store the return value.
    On failure, store the local variables where the exception was thrown.
    """
    def wrapped(self):
        if 'results' not in self.__dict__:
            self.results = {}
        # If a test throws an exception, store local variables in results:
        try:
            result = f(self)
        except Exception as e:
            self.results[f.__name__] = {'success':False, 'locals':inspect.trace()[-1][0].f_locals}
            raise e
        self.results[f.__name__] = {'success':True, 'result':result}
        return result
    return wrapped

def suite_results(suite):
    """
    Get all the results from a test suite
    """
    ans = {}
    for test in suite:
        if 'results' in test.__dict__:
            ans.update(test.results)
    return ans

# Example:
class TestSequenceFunctions(unittest.TestCase):

    def setUp(self):
        self.seq = range(10)

    @store_result
    def test_shuffle(self):
        # make sure the shuffled sequence does not lose any elements
        random.shuffle(self.seq)
        self.seq.sort()
        self.assertEqual(self.seq, range(10))
        # should raise an exception for an immutable sequence
        self.assertRaises(TypeError, random.shuffle, (1,2,3))
        return {1:2}

    @store_result
    def test_choice(self):
        element = random.choice(self.seq)
        self.assertTrue(element in self.seq)
        return {7:2}

    @store_result
    def test_sample(self):
        x = 799
        with self.assertRaises(ValueError):
            random.sample(self.seq, 20)
        for element in random.sample(self.seq, 5):
            self.assertTrue(element in self.seq)
        return {1:99999}


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

from pprint import pprint
pprint(suite_results(suite))

마지막 행은 테스트가 성공한 경우 반환된 값과 실패한 경우 로컬 변수(이 경우 x)를 인쇄합니다.

{'test_choice': {'result': {7: 2}, 'success': True},
 'test_sample': {'locals': {'self': <__main__.TestSequenceFunctions testMethod=test_sample>,
                            'x': 799},
                 'success': False},
 'test_shuffle': {'result': {1: 2}, 'success': True}}

어설션 오류에서 생성되는 예외를 탐지합니다.캐치 블록에서는 원하는 위치에 데이터를 출력할 수 있습니다.그런 다음 작업이 완료되면 예외를 다시 적용할 수 있습니다.테스트 주자는 아마 그 차이를 모를 것입니다.

고지 사항:나는 파이썬의 유닛 테스트 프레임워크로 이것을 시도해 본 적이 없지만, 다른 유닛 테스트 프레임워크로 시도해 본 적이 있습니다.

Facundo Casco의 답변을 확장하면, 이것은 저에게 꽤 효과적입니다.

class MyTest(unittest.TestCase):
    def messenger(self, message):
        try:
            self.assertEqual(1, 2, msg=message)
        except AssertionError as e:      
            print "\nMESSENGER OUTPUT: %s" % str(e),

언급URL : https://stackoverflow.com/questions/284043/outputting-data-from-unit-test-in-python