테스트란?
소프트웨어가 요구사항에 부합하여 개발되었는지 검증
유닛 테스트(unit tezt)란?
응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 의도된 대로 정확하게 작동하는지 검증
(장점)
1) 개발 과정 중에 문제를 미리 발견할 수 있다.
2) 코드 변경에 따른 영향도 파악이 용이하다.
3) 코드 리팩토링을 안심하고 할 수 있다.
4) 오래 전 짠 코드에 대한 파악 및 인수인계가 용이하다.
5) 테스팅에 대한 시간과 비용을 절감할 수 있다.
6) 새로운 기능 추가 시에 수시로 빠르게 테스트할 수 있다.
7) 코드에 대한 문서가 될 수 있다.
(단점)
1) 코드 유지 보수 관리가 늘어난다.
2) 다른 객체와 메세지를 주고 받는 객체의 경우 다른 객체 대신에 가짜 객체(Mock Object)를 주입하여 어떤 결과를 반환하도록 정해진 답변을 준비해야 하는 Stub가 발생한다.
1) 준비(Arrange, Given)
2) 실행(Act, When)
3) 검증(Assert, Then)
1) 리팩토링 내성
2) 테스트 결과 피드백의 스피드
3) 회귀 방지
4) 유지보수성
Fast: 테스트는 빠르게 동작하여 자주 돌릴 수 있어야 한다.
Independent: 각각의 테스트는 독립적이며 서로 의존해서는 안된다.
Repeatable: 어느 환경에서도 반복 가능해야 한다.
Self-Validating: 테스트는 성공 또는 실패로 bool 값으로 결과를 내어 자체적으로 검증되어야 한다.
Timely: 테스트는 적시에 즉, 테스트하려는 실제 코드를 구현하기 직전에 구현해야 한다.
1) 각 단위테스트는 독립적으로 이뤄지며(하나의 테스트 케이스에 대한 최소한 기능만 검증) 최대한 간경하게 작성한다.
1개의 테스트 함수에 대해 assert를 최소화하라
1개의 테스트 함수는 1가지 개념 만을 테스트하라
2) 입력값에 대한 결과 값을 검증하는 방식으로 작성한다.
3) Third Party Library의 기능은 제외한다.
1) 코드 수정하기 (테스트 코드 수정이나 예외처리보다는 코드에서 해결하자!)
2) 테스트 케이스 추가하기
테스트 픽스펴 : 1개 또는 그 이상의 테스트를 수행할 때 필요한 준비와 그와 관련된 정리 동작, 테스트 실행 환경(setUp(), tearDown())
테스트 케이스 : 테스트의 개별 단위
테스트 묶음 : 테스트 케이스, 테스트 묶음, 또는 둘 다의 모임으로 서로 같이 실행되어야 할 테스트들의 종합
테스트 실행자 : 테스트 실행을 조율하고 테스트 결과를 사용자에게 제공하는 역할을 하는 컴포넌트
주의사항
1) test*.py 파일로 별도로 빼서 관리 및 테스트를 권장한다.
2) 테스트 진행하는 함수는 함수명의 앞에 test*()로 만든다
step1 : 테스트 실행하여 성공 여부 보기
import unittest
def myfunc():
print(f'test 모듈 실행합니다')
class CustomTests(unittest.TestCase):
def test_runs(self):
myfunc()
if __name__ == '__main__':
unittest.main()
step1 실행 : python ./파일명.py
step1 결과
step2 : setUp() / tearDown() 사용하기
import unittest
def myfunc(num):
print(f'test 모듈 {num}번을 실행합니다')
class CustomTests(unittest.TestCase):
def setUp(self) -> None :
self.num = 5
def tearDown(self) -> None :
self.num = 10
def test_runs(self) -> None :
myfunc(self.num)
if __name__ == '__main__':
unittest.main()
step2 결과
step3 : 테스트 케이스 인자를 넣어서 결과 확인하기
import unittest
def myfunc(num):
print(f'test 모듈 {num}번을 실행합니다')
# return num # 주석 풀면 정상 실행 됨
class CustomTests(unittest.TestCase):
def setUp(self) -> None :
self.num = 5
def tearDown(self) -> None :
self.num = 10
def test_runs(self) -> None :
# assertEqual == 값이 같은 경우 Success , 다른 경우 Fail
self.assertEqual(myfunc(self.num), 5 , 'incorrect num')
if __name__ == '__main__':
unittest.main()
step3 : 결과1 - return을 주석처리한 경우 테스트 Fail 발생
step3 : 결과2 - return을 주석 해제한 경우 테스트 성공
메서드 | 검사하는 내용 | 추가된 버전 |
---|---|---|
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)가 exc를 발생 | |
assertRaisesRegex(exc, r, fun, *args, **kwds) | fun(*args, **kwds)가 exc를 발생하고 메시지가 정규식 r에 일치 | 3.1 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds)가 warn을 발생 | 3.2 |
assertWarnsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds)가 warn을 발생하고 메시지가 정규식 r에 일치 | 3.2 |
assertLogs(logger, level) | with 블록이 최소 level로 logger에 로그를 남김 | 3.4 |
assertNoLogs(logger, level) | The with block does not log on | logger with minimum level |
[참조-테스트 정의]
[참조-테스트 작성]
[참조-python unittest 모둘]