단위 테스트 (Unit Test)

단위 테스트는 독립적인 작은 단위의 기능을 테스트하는 방식이다. 단위 테스트에 필요한 코드를 따로 작성해두는 것은 각각의 기능이 잘 작동하는지 확인할 수 있는 좋은 습관이다. 

텍스트 픽스쳐(test fixture): 테스트 설정을 위한 코드

테스트 케이스(test case): 테스트의 기본 단위

테스트 스위트(test suite): 테스트 케이스의 집합 (unittest 모듈 참고)

테스트 러너(test runner): 테스트 스위트를 실행하는 객체


unittest

unittest는 테스트에 필요한 기능을 제공한다. unittest.TestCase를 상속 받은 클래스 내부에 test로 시작하는 메서드를 생성하면 테스트를 진행 할 수 있다. 클래스에 기본으로 생성되어 있는 메소드는 아래와 같다. 

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
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)

예시:

import unittest

class MathTest(unittest.TestCase):

    def test_add(self):
        self.assertEqual(3+3, 7)
    
    def test_add_true(self):
        self.assertTrue(3+5 == 8)

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

결과:

F.
======================================================================
FAIL: test_add (__main__.MathTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Users\music\Desktop\LAB\ex.py", line 6, in test_add
    self.assertEqual(3+3, 7)
AssertionError: 6 != 7

----------------------------------------------------------------------
Ran 2 tests in 0.004s

FAILED (failures=1)

테스트 2개를 실행하고, 테스트 여부, 에러 내용, 소요 시간 등 내용을 보여준다. 

테스트 케이스를 특정해서 추가할 수도 있다.

import unittest

class TestOne(unittest.TestCase):
    # 생략
    pass
    
class TestTwo(unittest.TestCase):
    # 생략
    pass

class TestThree(unittest.TestCase):
    # 생략
    pass

if __name__ == '__main__':
     # 테스트 케이스 생성
     case_1 = unittest.makeSuite(TestOne)
     case_2 = unittest.makeSuite(TestTwo)
     # suite 생성, 케이스 등록
     suite = unittest.TestSuite()
     suite.addTests((case_1, case_2))
     # 실행
     runner = unittest.TextTestRunner()
     runner.run(suite)

makeSuite로 테스트할 클래스를 인스턴스로 등록한다. TestSuite를 생성하고 등록한 후, runner로 실행시켜주면 된다. 위 예시의 경우, 등록되지 않은 TestThree의 메서드는 테스트되지 않는다. 


doctest

doctest는 docstring에 작성된 내용을 테스트해 주는 모듈이다. >>>뒤에 실행할 코드를 작성하고 아래 줄에 예상값을 적는다. 

def add_num(a, b):
    """Add two numbers
    
    >>> add_num(3, 6)
    9
    >>> add_num(0, 2)
    5
    """
    return a + b

if __name__ == "__main__":
    # 실행
    import doctest
    doctest.testmod()

실행할 때는 -v 옵션을 사용해야 테스트 내용을 확인할 수 있다. 

> python 파일이름.py -v
Trying:
    add_num(3, 6)
Expecting:
    9
ok
Trying:
    add_num(0, 2)
Expecting:
    5
**********************************************************************
File "ex.py", line 10, in __main__.add_num
Failed example:
    add_num(0, 2)
Expected:
    5
Got:
    2
1 items had no tests:
    __main__
**********************************************************************
1 items had failures:
   1 of   2 in __main__.add_num
2 tests in 2 items.
1 passed and 1 failed.
***Test Failed*** 1 failures.

예상값과 실행값이 같지 않으면 세부 내용을 표시해준다. 


pytest

pytest는 외부 모듈이기 때문에 설치가 필요하다. pytest는 test로 시작하는 파일을 찾아 test로 시작하는 함수를 실행한다. 

# test_math.py

def add_num(a, b):
    return a + b

def test_add_num():
    assert add_num(5, 3) == 8

아래 명령어를 통해 실행한다. (python -m pytest 또는 py.test)

> py.test
============================================= test session starts ==============================================
platform win32 -- Python 3.8.11, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: C:\Users\user\Desktop\LAB
collected 1 item

test_math.py .                                                                                            [100%] 

============================================== 1 passed in 0.04s ===============================================

실행한 디렉토리 내의 test_math.py를 찾아 실행한 모습이다.