어설 션이 실패 할 때 Python의 unittest에서 계속


84

편집 : 더 나은 예제로 전환하고 이것이 실제 문제인 이유를 명확히했습니다.

단일 테스트에서 여러 실패를 볼 수 있도록 어설 션이 실패 할 때 계속 실행되는 단위 테스트를 Python으로 작성하고 싶습니다. 예를 들면 :

class Car(object):
  def __init__(self, make, model):
    self.make = make
    self.model = make  # Copy and paste error: should be model.
    self.has_seats = True
    self.wheel_count = 3  # Typo: should be 4.

class CarTest(unittest.TestCase):
  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    self.assertEqual(car.make, make)
    self.assertEqual(car.model, model)  # Failure!
    self.assertTrue(car.has_seats)
    self.assertEqual(car.wheel_count, 4)  # Failure!

여기서 테스트의 목적은 Car가 __init__필드를 올바르게 설정 하는지 확인하는 것입니다. 나는 그것을 네 가지 방법으로 나눌 수있다 (그리고 그것은 종종 좋은 생각이다). 그러나이 경우 나는 그것을 하나의 개념을 테스트하는 하나의 방법으로 유지하는 것이 더 읽기 쉽다고 생각한다 ( "객체가 올바르게 초기화된다").

방법을 나누지 않는 것이 가장 좋다고 가정하면 새로운 문제가 있습니다. 모든 오류를 한 번에 볼 수 없습니다. model오류를 수정하고 테스트를 다시 실행하면 wheel_count오류가 나타납니다. 처음 테스트를 실행할 때 두 오류를 모두 보는 시간을 절약 할 수 있습니다.

비교를 위해 Google의 C ++ 단위 테스트 프레임 워크 치명적이지 않은 EXPECT_*어설 션과 치명적 ASSERT_*어설 션을 구분합니다.

단언은 동일한 것을 테스트하지만 현재 기능에 다른 영향을 미치는 쌍으로 제공됩니다. ASSERT_ * 버전은 실패하면 치명적인 실패를 생성하고 현재 기능을 중단합니다. EXPECT_ * 버전은 현재 기능을 중단하지 않는 치명적이지 않은 오류를 생성합니다. 일반적으로 EXPECT_ *는 테스트에서 둘 이상의 실패를보고 할 수 있으므로 선호됩니다. 그러나 문제의 주장이 실패 할 때 계속하는 것이 타당하지 않으면 ASSERT_ *를 사용해야합니다.

EXPECT_*파이썬에서 같은 동작 을 얻는 방법 이 unittest있습니까? 에없는 unittest경우이 동작을 지원하는 다른 Python 단위 테스트 프레임 워크가 있습니까?


덧붙여서, 치명적이지 않은 주장으로 얼마나 많은 실제 테스트가 도움이 될지 궁금해서 몇 가지 코드 예제를 살펴 보았습니다 (Google 코드 검색, RIP 대신 검색 코드를 사용하도록 2014-08-19 편집 됨). 첫 페이지에서 무작위로 선택된 10 개의 결과 중 모두 동일한 테스트 방법에서 여러 개의 독립적 인 주장을하는 테스트를 포함했습니다. 모든 사람은 치명적이지 않은 주장으로 이익을 얻을 수 있습니다.


2
당신은 무엇을하게 되었습니까? 저는이 주제에 관심이 있습니다 (댓글보다 더 넓은 공간에서 논의하고 싶은 완전히 다른 이유 때문에). 귀하의 경험을 알고 싶습니다. 그건 그렇고, "코드 예제"링크는 "슬프게도이 서비스가 종료되었습니다"로 끝납니다. 따라서 캐시 된 버전이 있다면 저도보고 싶습니다.
Davide

향후 참조를 위해 이것이 현재 시스템에서 동일한 검색 이라고 생각 하지만 결과는 더 이상 위에 설명 된 것과 다릅니다.
ZAD-Man

2
@Davide, 나는 아무것도하지 않았다. "방법 당 단 하나의 단언"접근 방식은 나에게 너무 엄격하게 독단적으로 보이지만 실행 가능한 (그리고 유지 관리 할 수있는) 유일한 해결책은 Anthony의 "catch and append"제안 인 것 같습니다. 그러나 그것은 나에게 너무 추한 일이다. 그래서 나는 메소드 당 여러 개의 assert를 고수했고 모든 실패를 찾는 데 필요한 것보다 더 많은 테스트를 실행해야 할 것이다.
Bruce Christensen

PyTest 라는 Python 테스트 프레임 워크 는 매우 직관적이며 기본적으로 모든 assert 실패를 보여줍니다. 그것은 당신이 직면 한 문제에 대한 해결 방법이 될 수 있습니다.
Surya Shekhar Chakraborty

답변:


9

unittest.TestCase어설 션이 실패 할 때 던지는 클래스이기 때문에 아마도 당신이하고 싶은 것은 파생하는 것입니다 . TestCase던지지 않도록 다시 설계해야 합니다 (대신 실패 목록을 유지하는 것이 좋습니다). 항목을 재구성하면 해결해야 할 다른 문제가 발생할 수 있습니다. 예를 들어 .NET Framework의 TestSuite변경 사항을 지원하기 위해 변경을 수행하기 위해 파생해야 할 수 있습니다 TestCase.


1
나는 이것이 아마도 궁극적 인 대답이 될 것이라고 생각했지만, 내 기지를 덮고 내가 빠진 것이 있는지 확인하고 싶었습니다. 감사!
Bruce Christensen 2011 년

4
TestCase소프트 어설 션을 구현하기 위해 재정의하는 것은 과잉이라고 말하고 싶습니다. 특히 파이썬에서 쉽게 만들 수 있습니다. 모든 AssertionErrors (아마도 간단한 루프)를 잡아서 목록이나 집합에 저장하십시오. , 한 번에 모두 실패합니다. 자세한 내용은 @Anthony Batchelor의 답변을 확인하십시오.
dcsordas 2013

2
@dscordas 일회성 테스트 용인지 또는 대부분의 테스트에서이 기능을 사용 하려는지에 따라 다릅니다.
dietbuddha 2013

43

치명적이지 않은 어설 션을 갖는 또 다른 방법은 어설 션 예외를 캡처하고 예외를 목록에 저장하는 것입니다. 그런 다음 해당 목록이 tearDown의 일부로 비어 있다고 주장합니다.

import unittest

class Car(object):
  def __init__(self, make, model):
    self.make = make
    self.model = make  # Copy and paste error: should be model.
    self.has_seats = True
    self.wheel_count = 3  # Typo: should be 4.

class CarTest(unittest.TestCase):
  def setUp(self):
    self.verificationErrors = []

  def tearDown(self):
    self.assertEqual([], self.verificationErrors)

  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    try: self.assertEqual(car.make, make)
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertEqual(car.model, model)  # Failure!
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertTrue(car.has_seats)
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertEqual(car.wheel_count, 4)  # Failure!
    except AssertionError, e: self.verificationErrors.append(str(e))

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

2
나는 당신에게 동의합니다. 이것이 Selenium이 파이썬 백엔드에서 확인 오류를 처리하는 방법입니다.
Anthony Batchelor

이 솔루션의 문제점은 모든 어설 션이 오류 (실패자가 아님)로 계산되고 오류를 렌더링하는 방법이 실제로 사용할 수 없다는 것입니다. 어쨌든 방법입니다 및 렌더링 기능은 easly 개선 될 수있다
eMarine

try / except 블록으로 모든 주장을 재정 의하여 dietbudda의 답변함께이 솔루션을 사용 unittest.TestCase하고 있습니다.
thodic

복잡한 테스트 패턴의 경우 이것은 unittest 오류를 이길 수있는 가장 좋은 솔루션이지만 모든 try / excepts로 테스트가 다소 추악하게 보입니다. 그것은 많은 테스트와 복잡한 단일 테스트 사이의 트레이드 오프입니다. 대신 오류 사전을 반환하기 시작했습니다. 그래서 하나의 테스트에서 전체 테스트 패턴을 테스트하고 동료 파이썬 개발자를 위해 가독성을 유지할 수 있습니다.
MortenB

이것은 매우 영리하므로 모자를 씁니다.
courtsimas 2018

30

한 가지 옵션은 모든 값에 대해 튜플로 한 번에 주장하는 것입니다.

예를 들면 :

class CarTest(unittest.TestCase):
  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    self.assertEqual(
            (car.make, car.model, car.has_seats, car.wheel_count),
            (make, model, True, 4))

이 테스트의 출력은 다음과 같습니다.

======================================================================
FAIL: test_init (test.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\temp\py_mult_assert\test.py", line 17, in test_init
    (make, model, True, 4))
AssertionError: Tuples differ: ('Ford', 'Ford', True, 3) != ('Ford', 'Model T', True, 4)

First differing element 1:
Ford
Model T

- ('Ford', 'Ford', True, 3)
?           ^ -          ^

+ ('Ford', 'Model T', True, 4)
?           ^  ++++         ^

이것은 모델과 바퀴 수가 모두 정확하지 않다는 것을 보여줍니다.


이것은 똑똑합니다. 지금까지 찾은 최고의 솔루션입니다.
Chen Ni

7

단일 단위 테스트에서 여러 개의 어설 션을 갖는 것은 안티 패턴으로 간주됩니다. 단일 단위 테스트는 한 가지만 테스트 할 것으로 예상됩니다. 너무 많이 테스트하고있을 수 있습니다. 이 테스트를 여러 테스트로 분할하는 것을 고려하십시오. 이렇게하면 각 테스트의 이름을 올바르게 지정할 수 있습니다.

그러나 때로는 여러 항목을 동시에 확인하는 것이 좋습니다. 예를 들어 동일한 객체의 속성을 주장 할 때. 이 경우 실제로 그 객체가 올바른지 여부를 주장합니다. 이를 수행하는 방법은 해당 객체에 대해 어설 션하는 방법을 알고있는 사용자 지정 도우미 메서드를 작성하는 것입니다. 실패한 모든 속성을 표시하거나 예를 들어 어설 션이 실패 할 때 예상되는 개체의 전체 상태와 실제 개체의 전체 상태를 표시하는 방식으로 해당 메서드를 작성할 수 있습니다.


1
@Bruce : 어설 션은 실패하거나 성공해야합니다. 그 사이에 아무것도. 테스트는 신뢰할 수 있고 읽기 쉽고 유지 관리 할 수 ​​있어야합니다. 테스트에 실패하지 않는 실패한 주장은 나쁜 생각입니다. 그것은 당신의 테스트를 지나치게 복잡하게 만들고 (가독성과 유지 보수성을 떨어 뜨린다) '실패 할 수있는'테스트를 가짐으로써 그것들을 쉽게 무시할 수있게하여 신뢰할 수 없다는 것을 의미합니다.
Steven

8
나머지 테스트를 실행할 수 없으며 여전히 치명적입니다. 발생할 수있는 모든 가능한 실패를 집계하기 위해 어딘가에서 실패의 반환을 지연시킬 수 있다고 생각합니다.
dietbuddha 2011 년

5
우리 둘 다 같은 말을하는 것 같아요. 나는 모든 실패한 주장이 테스트를 실패하게하기를 원합니다. @dietbuddha가 언급했듯이 assert가 테스트 될 때 즉시가 아니라 테스트 메서드가 반환 될 때 실패가 발생하기를 원합니다. 이렇게 하면 메서드의 모든 어설 션을 테스트 할 수 있으므로 한 번에 모든 오류를 확인하고 수정할 수 있습니다. 테스트는 여전히 신뢰할 수 있고 읽기 쉽고 유지 관리가 가능합니다 (실제로 더 그렇습니다).
Bruce Christensen 2011 년

10
그는 당신이 어설 션을 쳤을 때 테스트가 실패해서는 안된다고 말하는 것이 아니라 실패가 다른 검사를 방해해서는 안된다고 말하는 것입니다. 예를 들어, 지금은 특정 디렉토리가 사용자, 그룹 및 기타 쓰기 가능 여부를 테스트하고 있습니다. 각각은 별도의 주장입니다. 테스트 출력에서 ​​세 가지 케이스가 모두 실패했음을 아는 것이 유용 할 것입니다. 따라서 "Path is not user-writable"을 가져 오는 대신 한 번의 chmod 호출로 문제를 해결할 수 있습니다. 테스트를 다시 실행하여 "Path is 그룹 쓰기 가능하지 않음 "등입니다. 나는 그들이 별도의 테스트가되어야한다고 주장했지만 ...
Tim Keating

8
라이브러리가 unittest라고해서 테스트가 분리 된 단위 테스트라는 의미는 아닙니다. unittest 모듈과 pytest, nose 및 기타 모듈은 시스템 테스트, 통합 테스트 등에 적합합니다. 단 한 번만 실패 할 수 있다는 경고가 있습니다. 정말 짜증납니다. 나는 모든 assert 함수가 실패를 계속할 수있는 매개 변수를 추가하거나 이러한 일을하는 expectBlah라는 assert 함수의 복제를 정말로보고 싶습니다. 그러면 unittest로 더 큰 기능 테스트를 작성하는 것이 더 쉬울 것입니다.
Okken dec

7

Python 3.4부터 하위 테스트 를 사용할 수도 있습니다 .

def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    with self.subTest(msg='Car.make check'):
        self.assertEqual(car.make, make)
    with self.subTest(msg='Car.model check'):
        self.assertEqual(car.model, model)
    with self.subTest(msg='Car.has_seats check'):
        self.assertTrue(car.has_seats)
    with self.subTest(msg='Car.wheel_count check'):
        self.assertEqual(car.wheel_count, 4)

( msg매개 변수는 실패한 테스트를보다 쉽게 ​​판별하는 데 사용됩니다.)

산출:

======================================================================
FAIL: test_init (__main__.CarTest) [Car.model check]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 23, in test_init
    self.assertEqual(car.model, model)
AssertionError: 'Ford' != 'Model T'
- Ford
+ Model T


======================================================================
FAIL: test_init (__main__.CarTest) [Car.wheel_count check]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 27, in test_init
    self.assertEqual(car.wheel_count, 4)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=2)

1
이것은 이제 기존 코드에 가장 쉽게 적용 할 수있는 대답이되어야합니다.
Michael Scott Cuthbert

5

별도의 방법으로 각 assert를 수행하십시오.

class MathTest(unittest.TestCase):
  def test_addition1(self):
    self.assertEqual(1 + 0, 1)

  def test_addition2(self):
    self.assertEqual(1 + 1, 3)

  def test_addition3(self):
    self.assertEqual(1 + (-1), 0)

  def test_addition4(self):
    self.assertEqaul(-1 + (-1), -1)

5
이것이 가능한 해결책이라는 것을 알고 있지만 항상 실용적인 것은 아닙니다. 이전에 응집력이 있었던 테스트를 몇 가지 작은 방법으로 분해하지 않고 작동하는 것을 찾고 있습니다.
Bruce Christensen

@Bruce Christensen : 그들이 너무 응집력이 있다면 아마도 이야기를 형성할까요? 그런 다음 doctest로 만들 수 있으며 실제로 실패 후에도 계속 됩니다 .
Lennart Regebro

1
다음과 같은 테스트 세트가 있습니다. 1. 데이터로드, 2. 데이터가 올바르게로드 됨, 3. 데이터 수정, 4. 수정이 올바르게 작동 함, 5. 수정 된 데이터 저장, 6. 데이터가 올바르게 저장되었는지 확인합니다. 이 방법으로 어떻게 할 수 있습니까? setup()테스트 중 하나이기 때문에 에서 데이터를로드하는 것은 이치에 맞지 않습니다 . 그러나 각 주장을 자체 기능에 넣으면 데이터를 3 번로드해야하는데 이는 엄청난 리소스 낭비입니다. 그런 상황을 처리하는 가장 좋은 방법은 무엇입니까?
naught101

음, 특정 시퀀스를 테스트하는 테스트는 동일한 테스트 방법에 있어야합니다.
Lennart Regebro

4

PyPI에는 softest요구 사항을 처리 할 소프트 어설 션 패키지 가 있습니다. 실패를 수집하고, 예외와 스택 추적 데이터를 결합하고, 모든 것을 일반적인 unittest출력의 일부로보고하는 방식으로 작동합니다 .

예를 들어이 코드는 다음과 같습니다.

import softest

class ExampleTest(softest.TestCase):
    def test_example(self):
        # be sure to pass the assert method object, not a call to it
        self.soft_assert(self.assertEqual, 'Worf', 'wharf', 'Klingon is not ship receptacle')
        # self.soft_assert(self.assertEqual('Worf', 'wharf', 'Klingon is not ship receptacle')) # will not work as desired
        self.soft_assert(self.assertTrue, True)
        self.soft_assert(self.assertTrue, False)

        self.assert_all()

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

...이 콘솔 출력을 생성합니다.

======================================================================
FAIL: "test_example" (ExampleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 14, in test_example
    self.assert_all()
  File "C:\...\softest\case.py", line 138, in assert_all
    self.fail(''.join(failure_output))
AssertionError: ++++ soft assert failure details follow below ++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The following 2 failures were found in "test_example" (ExampleTest):
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Failure 1 ("test_example" method)
+--------------------------------------------------------------------+
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 10, in test_example
    self.soft_assert(self.assertEqual, 'Worf', 'wharf', 'Klingon is not ship receptacle')
  File "C:\...\softest\case.py", line 84, in soft_assert
    assert_method(*arguments, **keywords)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 829, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 1203, in assertMultiLineEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: 'Worf' != 'wharf'
- Worf
+ wharf
 : Klingon is not ship receptacle

+--------------------------------------------------------------------+
Failure 2 ("test_example" method)
+--------------------------------------------------------------------+
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 12, in test_example
    self.soft_assert(self.assertTrue, False)
  File "C:\...\softest\case.py", line 84, in soft_assert
    assert_method(*arguments, **keywords)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 682, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

참고 : softest.


3

예상은 gtest에서 매우 유용합니다. 이것은 gist 및 코드 에서 파이썬 방식입니다 .

import sys
import unittest


class TestCase(unittest.TestCase):
    def run(self, result=None):
        if result is None:
            self.result = self.defaultTestResult()
        else:
            self.result = result

        return unittest.TestCase.run(self, result)

    def expect(self, val, msg=None):
        '''
        Like TestCase.assert_, but doesn't halt the test.
        '''
        try:
            self.assert_(val, msg)
        except:
            self.result.addFailure(self, sys.exc_info())

    def expectEqual(self, first, second, msg=None):
        try:
            self.failUnlessEqual(first, second, msg)
        except:
            self.result.addFailure(self, sys.exc_info())

    expect_equal = expectEqual

    assert_equal = unittest.TestCase.assertEqual
    assert_raises = unittest.TestCase.assertRaises


test_main = unittest.main

2

AssertionError 예외를 캡처하기 위해 @ Anthony-Batchelor의 접근 방식이 마음에 들었습니다. 그러나 데코레이터를 사용하는이 접근 방식에 약간의 변형이 있으며 통과 / 실패로 테스트 사례를보고하는 방법도 있습니다.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest

class UTReporter(object):
    '''
    The UT Report class keeps track of tests cases
    that have been executed.
    '''
    def __init__(self):
        self.testcases = []
        print "init called"

    def add_testcase(self, testcase):
        self.testcases.append(testcase)

    def display_report(self):
        for tc in self.testcases:
            msg = "=============================" + "\n" + \
                "Name: " + tc['name'] + "\n" + \
                "Description: " + str(tc['description']) + "\n" + \
                "Status: " + tc['status'] + "\n"
            print msg

reporter = UTReporter()

def assert_capture(*args, **kwargs):
    '''
    The Decorator defines the override behavior.
    unit test functions decorated with this decorator, will ignore
    the Unittest AssertionError. Instead they will log the test case
    to the UTReporter.
    '''
    def assert_decorator(func):
        def inner(*args, **kwargs):
            tc = {}
            tc['name'] = func.__name__
            tc['description'] = func.__doc__
            try:
                func(*args, **kwargs)
                tc['status'] = 'pass'
            except AssertionError:
                tc['status'] = 'fail'
            reporter.add_testcase(tc)
        return inner
    return assert_decorator



class DecorateUt(unittest.TestCase):

    @assert_capture()
    def test_basic(self):
        x = 5
        self.assertEqual(x, 4)

    @assert_capture()
    def test_basic_2(self):
        x = 4
        self.assertEqual(x, 4)

def main():
    #unittest.main()
    suite = unittest.TestLoader().loadTestsFromTestCase(DecorateUt)
    unittest.TextTestRunner(verbosity=2).run(suite)

    reporter.display_report()


if __name__ == '__main__':
    main()

콘솔 출력 :

(awsenv)$ ./decorators.py 
init called
test_basic (__main__.DecorateUt) ... ok
test_basic_2 (__main__.DecorateUt) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
=============================
Name: test_basic
Description: None
Status: fail

=============================
Name: test_basic_2
Description: None
Status: pass

1

@Anthony Batchelor 의 답변에 문제가 있었는데 try...catch내 단위 테스트 내 에서 사용하도록 강요했기 때문입니다 . 대신 메서드 try...catch재정의에 논리를 캡슐화했습니다 TestCase.assertEqual. 다음은 코드입니다.

import unittest
import traceback

class AssertionErrorData(object):

    def __init__(self, stacktrace, message):
        super(AssertionErrorData, self).__init__()
        self.stacktrace = stacktrace
        self.message = message

class MultipleAssertionFailures(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        self.verificationErrors = []
        super(MultipleAssertionFailures, self).__init__( *args, **kwargs )

    def tearDown(self):
        super(MultipleAssertionFailures, self).tearDown()

        if self.verificationErrors:
            index = 0
            errors = []

            for error in self.verificationErrors:
                index += 1
                errors.append( "%s\nAssertionError %s: %s" % ( 
                        error.stacktrace, index, error.message ) )

            self.fail( '\n\n' + "\n".join( errors ) )
            self.verificationErrors.clear()

    def assertEqual(self, goal, results, msg=None):

        try:
            super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )

        except unittest.TestCase.failureException as error:
            goodtraces = self._goodStackTraces()
            self.verificationErrors.append( 
                    AssertionErrorData( "\n".join( goodtraces[:-2] ), error ) )

    def _goodStackTraces(self):
        """
            Get only the relevant part of stacktrace.
        """
        stop = False
        found = False
        goodtraces = []

        # stacktrace = traceback.format_exc()
        # stacktrace = traceback.format_stack()
        stacktrace = traceback.extract_stack()

        # /programming/54499367/how-to-correctly-override-testcase
        for stack in stacktrace:
            filename = stack.filename

            if found and not stop and \
                    not filename.find( 'lib' ) < filename.find( 'unittest' ):
                stop = True

            if not found and filename.find( 'lib' ) < filename.find( 'unittest' ):
                found = True

            if stop and found:
                stackline = '  File "%s", line %s, in %s\n    %s' % ( 
                        stack.filename, stack.lineno, stack.name, stack.line )
                goodtraces.append( stackline )

        return goodtraces

# class DummyTestCase(unittest.TestCase):
class DummyTestCase(MultipleAssertionFailures):

    def setUp(self):
        self.maxDiff = None
        super(DummyTestCase, self).setUp()

    def tearDown(self):
        super(DummyTestCase, self).tearDown()

    def test_function_name(self):
        self.assertEqual( "var", "bar" )
        self.assertEqual( "1937", "511" )

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

결과 출력 :

F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 77, in tearDown
    super(DummyTestCase, self).tearDown()
  File "D:\User\Downloads\test.py", line 29, in tearDown
    self.fail( '\n\n' + "\n\n".join( errors ) )
AssertionError: 

  File "D:\User\Downloads\test.py", line 80, in test_function_name
    self.assertEqual( "var", "bar" )
AssertionError 1: 'var' != 'bar'
- var
? ^
+ bar
? ^
 : 

  File "D:\User\Downloads\test.py", line 81, in test_function_name
    self.assertEqual( "1937", "511" )
AssertionError 2: '1937' != '511'
- 1937
+ 511
 : 

올바른 스택 추적 캡처를위한 더 많은 대체 솔루션은 올바른 스택 추적을 생성하는 TestCase.assertEqual ()을 올바르게 재정의하는 방법에 게시 될 수 있습니다 .


0

나는 PyUnit으로 이것을 할 방법이 없다고 생각하며 PyUnit이 이런 식으로 확장되는 것을보고 싶지 않을 것입니다.

나는 테스트 함수 당 하나의 주장을 고수하는 것을 선호하며 ( 또는 더 구체적으로 테스트 당 하나의 개념을 주장 ) test_addition()4 개의 개별 테스트 함수로 다시 작성 합니다. 이것은 실패에 대한 유용한 정보를 줄 것 즉를 :

.FF.
======================================================================
FAIL: test_addition_with_two_negatives (__main__.MathTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_addition.py", line 10, in test_addition_with_two_negatives
    self.assertEqual(-1 + (-1), -1)
AssertionError: -2 != -1

======================================================================
FAIL: test_addition_with_two_positives (__main__.MathTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_addition.py", line 6, in test_addition_with_two_positives
    self.assertEqual(1 + 1, 3)  # Failure!
AssertionError: 2 != 3

----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=2)

이 접근 방식이 귀하에게 적합하지 않다고 판단되면 이 답변이 도움 될 수 있습니다.

최신 정보

업데이트 된 질문으로 두 가지 개념을 테스트하는 것 같습니다.이를 두 개의 단위 테스트로 나누었습니다. 첫 번째는 새 개체를 만들 때 매개 변수가 저장된다는 것입니다. 이것은 두 개의 주장을 가질 것 make입니다.model . 첫 번째가 실패하면 두 번째가 통과하든 실패하든이 시점에서 문제가 명확하게 해결되어야합니다.

두 번째 개념은 더 의심 스럽습니다. 일부 기본값이 초기화되었는지 테스트하고 있습니다. ? 실제로 사용되는 지점에서 이러한 값을 테스트하는 것이 더 유용 할 것입니다 (사용되지 않는 경우 왜 거기에 있습니까?).

이 두 테스트는 모두 실패하며 둘 다 실패합니다. 단위 테스트를 할 때 집중해야하는 부분이기 때문에 성공보다는 실패에 훨씬 더 관심이 있습니다.

FF
======================================================================
FAIL: test_creation_defaults (__main__.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_car.py", line 25, in test_creation_defaults
    self.assertEqual(self.car.wheel_count, 4)  # Failure!
AssertionError: 3 != 4

======================================================================
FAIL: test_creation_parameters (__main__.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_car.py", line 20, in test_creation_parameters
    self.assertEqual(self.car.model, self.model)  # Failure!
AssertionError: 'Ford' != 'Model T'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=2)

그렇다면 Car.test_init를 4 개의 함수로 나누시겠습니까?
Bruce Christensen

@Bruce Christensen : 아마 두 개로 나눌 것입니다. 그러나 그럼에도 불구하고 나는 당신의 주장이 유용하다고 확신하지 않습니다. 답변하려면 업데이트를 참조하십시오.
Johnsyweb

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.