아래 내용은 공부한 것을 정리하므로 틀린 내용이 포함되어 있을 수 있습니다.
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 |
참조 사이트
- 파이썬 공식 문서 - https://docs.python.org/3/library/unittest.html
- 조대협님의 블로그 - http://bcho.tistory.com/86
- 파이썬을 여행하는 히치하이커 - https://python-guide-kr.readthedocs.io/ko/latest/writing/tests.html