Notebook 샘플 코드는 최종 파일만 배포합니다
(주:booksearch_module.py) import unittest # 애플리케이션 코드 def booksearch(): # 임의 처리 return {} class BookSearchTest(unittest.TestCase): # booksearch() 테스트 코드 def test_booksearch(self): self.assertEqual({}, booksearch())
$ python3 -m unittest ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK
(주:tests/test_api.py) import unittest class BuildUrlTest(unittest.TestCase): def test_build_url(self): # build_url()이 테스트 대상 처리 from booksearch.api import build_url expected = 'https://www.googleapis.com/books/v1/volumes?q=python' actual = build_url({'q': 'python'}) # assert 메서드 이용 self.assertEqual(expected, actual)(주:tests/test_api.py) (생략) actual = build_url({'q': 'python'}) # assert 메서드 이용 self.assertEqual(expected, actual) # 좋지 않은 예시 # 위 행이 실패하면 아래 행이 실행되지 않음 expected2 = 'https://www.googleapis.com/books/v1/volumes?' actual2 = build_url({}) self.assertEqual(expected2, actual2)(주:tests/test_api.py) (생략) actual = build_url({'q': 'python'}) # assert 메서드 이용 self.assertEqual(expected, actual) # 좋은 예 def test_build_url_empty_param(self): from booksearch.api import build_url expected = 'https://www.googleapis.com/books/v1/volumes?' actual = build_url({}) self.assertEqual(expected, actual)
(주:tests/test_core.py) import pathlib import unittest from tempfile import TemporaryDirectory THUMBNAIL_URL = ( 'http://books.google.com/books/content' '?id=OgtBw76OY5EC&printsec=frontcover' '&img=1&zoom=1&edge=curl&source=gbs_api' ) class SaveThumbnailsTest(unittest.TestCase): def setUp(self): # 임시 디렉터리 작성 self.tmp = TemporaryDirectory() def tearDown(self): # 임시 디렉터리 정리 self.tmp.cleanup() def test_save_thumbnails(self): from booksearch.core import Book book = Book({'id': '', 'volumeInfo': { 'imageLinks': { 'thumbnail': THUMBNAIL_URL }}}) # 처리를 실행하고 파일이 작성되었음을 확인 filename = book.save_thumbnails(self.tmp.name)[0] self.assertTrue(pathlib.Path(filename).exists())
$ python3 -m unittest ... ---------------------------------------------------------------------- Ran 3 tests in 0.899s OK$ python3 -m unittest -v test_build_url (test_api.BuildUrlTest) ... ok test_build_url_empty_param (test_api.BuildUrlTest) ... ok test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.337s OK
(주:tests/test_api.py) class BuildUrlTest(unittest.TestCase): (생략) def test_build_url_fail(self): from booksearch.api import build_url expected = 'https://www.googleapis.com/books/v1/volumes' actual = build_url({}) self.assertEqual(expected, actual, msg='이 테스트는 실패합니다')$ python3 -m unittest -v test_build_url (test_api.BuildUrlTest) ... ok test_build_url_empty_param (test_api.BuildUrlTest) ... ok test_build_url_fail (test_api.BuildUrlTest) ... FAIL test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ====================================================================== FAIL: test_build_url_fail (test_api.BuildUrlTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/.../workspace/tests/test_api.py", line 24, in test_build_url_fail msg='이 테스트는 실패합니다') AssertionError: 'https://www.googleapis.com/books/v1/volumes' != 'https://www.googleapis.com/books/v1/volumes?' - https://www.googleapis.com/books/v1/volumes + https://www.googleapis.com/books/v1/volumes? ? + : 이 테스트는 실패합니다 ---------------------------------------------------------------------- Ran 4 tests in 0.397s FAILED (failures=1)
(주:tests/test_api.py) class BuildUrlTest(unittest.TestCase): (생략) @unittest.expectedFailure def test_build_url_fail(self):$ python3 -m unittest -v test_build_url (test_api.BuildUrlTest) ... ok test_build_url_empty_param (test_api.BuildUrlTest) ... ok test_build_url_fail (test_api.BuildUrlTest) ... expected failure test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.400s OK (expected failures=1)
# 모듈 지정 $ python3 -m unittest -v tests.test_api test_build_url (tests.test_api.BuildUrlTest) ... ok test_build_url_empty_param (tests.test_api.BuildUrlTest) ... ok test_build_url_fail (tests.test_api.BuildUrlTest) ... expected failure ---------------------------------------------------------------------- Ran 3 tests in 0.049s OK (expected failures=1) # 클래스 지정 $ python3 -m unittest -v tests.test_api.BuildUrlTest (생략) # 메서드 지정 $ python3 -m unittest -v tests.test_api.BuildUrlTest.test_build_url (생략) # 여러 테스트 지정 $ python3 -m unittest -v tests.test_api tests.test_core (생략)
# 실행할 테스트 모듈 이름을 지정 $ python3 -m unittest discover -s tests -p 'test_c*.py' -v test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.362s OK
(주:tests/test_core.py) from unittest.mock import patch (생략) class SaveThumbnailsTest(unittest.TestCase): (생략) # 테스트 대상의 save_thumbnail()가 이용할 참조 이름을 지정 @patch('booksearch.core.get_data') def test_save_thumbnails(self, mock_get_data): from booksearch.core import Book # 앞에서 얻은 섬네일 이미지 데이터를 모의 객치의 반환값으로 설정 data_path = pathlib.Path(__file__).with_name('data') mock_get_data.return_value = ( data_path / 'YkGmfbil6L4C_thumbnail.jpeg').read_bytes() book = Book({'id': '', 'volumeInfo': { 'imageLinks': { 'thumbnail': THUMBNAIL_URL }}}) filename = book.save_thumbnails(self.tmp.name)[0] # get_data() 호출 시의 인수 확인 mock_get_data.assert_called_with(THUMBNAIL_URL) # 저장된 데이터 확인 self.assertEqual(data, filename.read_bytes())$ python3 -m unittest -v test_build_url (test_api.BuildUrlTest) ... ok test_build_url_empty_param (test_api.BuildUrlTest) ... ok test_build_url_fail (test_api.BuildUrlTest) ... expected failure test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.040s OK (expected failures=1)
(주:tests/test_api.py) import sys (생략) class BuildUrlTest(unittest.TestCase): (생략) # 인수로 테스트를 건너 뛰는 이유를 전달 @unittest.skip('this is a skip test') def test_nothing_skip(self): pass # 실행 중인 파이썬 버전이 3.6보다 높으면 건너뜀 @unittest.skipIf(sys.version_info > (3, 6), 'this is a skipIf test') def test_nothing_skipIf(self): pass$ python3 -m unittest -v test_build_url (test_api.BuildUrlTest) ... ok test_build_url_empty_param (test_api.BuildUrlTest) ... ok test_build_url_fail (test_api.BuildUrlTest) ... expected failure test_nothing_skip (test_api.BuildUrlTest) ... skipped 'this is a skip test' test_nothing_skipIf (test_api.BuildUrlTest) ... skipped 'this is a skipIf test' test_save_thumbnails (test_core.SaveThumbnailsTest) ... ok ---------------------------------------------------------------------- Ran 6 tests in 0.038s OK (skipped=2, expected failures=1)
(주:tests/test_core.py) from urllib.error import URLError (생략) class GetBooksTest(unittest.TestCase): def test_get_books_no_connection(self): from booksearch.core import get_books # 임시로 네트워크 접속 단절 with patch('socket.socket.connect') as mock: # connect()가 호출되면 정확하지 않은 값을 반환함 mock.return_value = None with self.assertRaisesRegex(URLError, 'urlopen error'): # 예외가 발생하는 처리를 with 블록 안에서 실행 get_books(q='python')
(주:tests/test_api.py) (생략) class BuildUrlMultiTest(unittest.TestCase): def test_build_url_multi(self): from booksearch.api import build_url base = 'https://www.googleapis.com/books/v1/volumes?' expected_url = f'{base}q=python' # 2번째, 3번째 테스트는 실패함 params = ( (expected_url, {'q': 'python'}), (expected_url, {'q': 'python', 'maxResults': 1}), (expected_url, {'q': 'python', 'langRestrict': 'en'}), ) for expected, param in params: with self.subTest(**param): actual = build_url(param) self.assertEqual(expected, actual)
(주:tests/test_api.py) import json from io import StringIO from unittest.mock import patch, MagicMock (생략) class GetJsonTest(unittest.TestCase): def test_get_json(self): from booksearch.api import get_json with patch('booksearch.api.request.urlopen') as mock_urlopen: # 컨텍스트 관리자의 목(mock)을 준비 # API 응답이 될 JSON 데이터 준비 expected_response = {'id': 'test'} fp = StringIO(json.dumps(expected_response)) # MagicMock 클래스를 사용하면 __exit__를 추가할 필요 없음 mock = MagicMock() mock.__enter__.return_value = fp # urlopen()의 반환값이 컨텍스트 관리자 mock_urlopen.return_value = mock actual = get_json({'q': 'python'}) self.assertEqual(expected_response, actual)