Python(파이썬) 기본 - 50. unittest - 단위테스트

아래 내용은 공부한 것을 정리하므로 틀린 내용이 포함되어 있을 수 있습니다.

1. 테스트와 단위테스트

  • 테스트란? - 소프트웨어가 요구사항에 의해 개발된 산출물이 요구사항과 부합하는지 여부를 검증하기 위한 작업
  • 단위 테스트란? - 모듈 또는 응용 프로그램 내의 개별 코드 단위가 예상대로 작동하는지 확인하는 반복 가능한 활동

2. unittest

  • Python에 포함된 다양한 테스트를 자동화할 수 있는 기능이 포함되어 있는 표준 라이브러리
  • unittest에 포함된 주요 개념
    • TestCase : unittest 프레임 워크의 테스트 조직의 기본 단위
    • Fixture :
      • 테스트함수의 전 또는 후에 실행
      • 테스트가 실행되기 전에 테스트 환경이 예상 된 상태에 있는지 확인하는 데 사용
      • 테스트 전에 데이터베이스 테이블을 만들거나 테스트 후에 사용한 리소스를 정리하는데 사용
    • assertion :
      • unittest가 테스트가 통과하는지 또는 실패 하는지를 결정.
      • bool test, 객체의 적합성, 적절한 예외 발생 등 다양한 점검을 할 수 있음
      • assertion이 실패하면 테스트 함수가 실패합니다.

3. unittest 모듈의 사용

  • 예제 코드를 작성합니다.
  • test.py 파일을 만들고, 코드를 아래와 같이 작성합니다.
  • TestCase 를 작성하기 위해 unittest.TestCase를 상속한 테스트 클래스를 작성합니다.
  • test_ 라는 이름으로 시작하는 메소드는 모두 테스트 메소드가 됩니다.
  • test_run() 메소드는 단순 실행여부만 판별합니다.
  • unittest.main() 코드를 통해 테스트가 수행됩니다.
import unittest


# TestCase를 작성
class CustomTests(unittest.TestCase): 

    def test_runs(self):
        """단순 실행여부 판별하는 테스트 메소드"""
        
        custom_function()


# unittest를 실행
if __name__ == '__main__':  
    unittest.main()
  • 저장 후 터미널 화면에서 아래와 같이 실행해봅니다.
  • 1개의 test가 실행되며 custom_function() 를 작성하지 않았기에 실패되었습니다.
$ python test.py
E
======================================================================
ERROR: test_runs (__main__.CustomTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 7, in test_runs
    custom_function()
NameError: name 'custom_function' is not defined

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

FAILED (errors=1)

  • custom_function() 함수를 작성합니다.
import unittest


def custom_function():
    pass

# TestCase를 작성
class CustomTests(unittest.TestCase):
... 생략
  • 다시 터미널에서 실행해봅니다.
$ python test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

  • 파일을 하나 작성하고, 해당 파일을 검사하는 테스트를 추가하겠습니다.
  • setUp() : Fixture 입니다. 테스트 전에 수행됩니다.
  • tearDown() : Fixture입니다. 테스트 후에 수행됩니다.
  • setUp() 에서 파일을 생성하고, tearDown() 에서 파일을 삭제합니다.
  • custom_function() 함수는 file_name을 인수로 받아 파일의 라인을 합산해서 리턴합니다.
  • self.assertEqual(custom_function(self.file_name), 3) 구문은 custom_function(file_name) 을 수행하고 결과가 3이면 테스트를 통과합니다.
import unittest
import os


def custom_function(file_name):
    with open(file_name, 'rt') as f:
        return sum(1 for _ in f)


# TestCase를 작성
class CustomTests(unittest.TestCase):

    def setUp(self):
        """테스트 시작되기 전 파일 작성"""
        self.file_name = 'test_file.txt'
        with open(self.file_name, 'wt') as f:
            f.write("""
            파이썬에는 정말 단위테스트 모듈이 기본으로 포함되어 있나요? 진짜?
            멋지군요!
            단위테스트를 잘 수행해보고 싶습니다!
            """.strip())

    def tearDown(self):
        """테스트 종료 후 파일 삭제 """
        try:
            os.remove(self.file_name)
        except:
            pass

    def test_runs(self):
        """단순 실행여부 판별하는 테스트 메소드"""

        custom_function(self.file_name)

    def test_line_count(self):
        self.assertEqual(custom_function(self.file_name), 3)


# unittest를 실행
if __name__ == '__main__':
    unittest.main()
  • 테스트를 실행해봅니다.
  • 2개의 테스트가 실행되어 성공되었음이 표시됩니다.
$ python test.py      
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

  • 일부러 에러를 발생시켜 의도한 에러가 발생하는지 살펴봅니다.
  • 다음 test메소드를 추가합니다.
  • 에러를 확인하는 테스트는 assertRaises()메소드를 사용하며 with 문으로 감싸면서 내부의 코드가 에러가 발생하는 지를 확인합니다.
... 생략
class CustomTests(unittest.TestCase):
    ... 생략
    def test_no_file(self):
        with self.assertRaises(IOError):
            custom_function(self.file_name)  
    ... 생략
  • 테스트를 실행합니다.
  • 3개의 테스트 실행 중 1개가 실패했다는 문구가 나옵니다.
  • 3번째 테스트는 에러가 발생해야 테스트가 성공합니다. 의도한 에러가 나는지 확인하는 테스트입니다.
$ python test.py
.F.
======================================================================
FAIL: test_no_file (__main__.CustomTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 40, in test_no_file
    custom_function('test_file.txt')
AssertionError: OSError not raised

----------------------------------------------------------------------
Ran 3 tests in 0.006s

FAILED (failures=1)
  • 3번째 테스트메소드에서 의도적으로 에러를 발생시키기위해 인자를 변경합니다.
...생략
def test_no_file(self):
        with self.assertRaises(IOError):
            custom_function('abc.txt')  
...생략
  • 3가지테스트 모두 성공을 확인할 수 있습니다.
$ python test.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.003s

OK

4. unittest의 assert메소드 리스트

Method Checks that New in
assertEqual(a, b) a == b  
assertNotEqual(a, b) a != b  
assertTrue(x) bool(x) is True  
assertFalse(x) bool(x) is False  
assertIs(a, b) a is b 3.1
assertIsNot(a, b) a is not b 3.1
assertIsNone(x) x is None 3.1
assertIsNotNone(x) x is not None 3.1
assertIn(a, b) a in b 3.1
assertNotIn(a, b) a not in b 3.1
assertIsInstance(a, b) isinstance(a, b) 3.2
assertNotIsInstance(a, b) not isinstance(a, b) 3.2
assertRaises(exc, fun, *args, **kwds) fun(*args, **kwds) raises exc  
assertRaisesRegex(exc, r, fun, *args, **kwds) fun(*args, **kwds) raises exc and the message matches regex r 3.1
assertWarns(warn, fun, *args, **kwds) fun(*args, **kwds) raises warn 3.2
assertWarnsRegex(warn, r, fun, *args, **kwds) fun(*args, **kwds) raises warn and the message matches regex r 3.2
assertLogs(logger, level) The with block logs on logger with minimum level 3.4
assertAlmostEqual(a, b) round(a-b, 7) == 0  
assertNotAlmostEqual(a, b) round(a-b, 7) != 0  
assertGreater(a, b) a > b 3.1
assertGreaterEqual(a, b) a >= b 3.1
assertLess(a, b) a < b 3.1
assertLessEqual(a, b) a <= b 3.1
assertRegex(s, r) r.search(s) 3.1
assertNotRegex(s, r) not r.search(s) 3.2
assertCountEqual(a, b) a and b have the same elements in the same number, regardless of their order 3.2
assertMultiLineEqual(a, b) strings 3.1
assertSequenceEqual(a, b) sequences 3.1
assertListEqual(a, b) lists 3.1
assertTupleEqual(a, b) tuples 3.1
assertSetEqual(a, b) sets or frozensets 3.1
assertDictEqual(a, b) dicts 3.1

참조 사이트