Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Python 테스트 시작하기

7,267 views

Published on

Pycon Korea 2015 발표자료 입니다.

Published in: Software

Python 테스트 시작하기

  1. 1. Python 테스트 시작하기 이호성 에서 함께 보실 수 있습니다 http://slides.com/hosunglee-1/deck/live
  2. 2. 누구세요누구세요??
  3. 3. 이호성이호성 Enswers DevOps Python, Cloud Computing 함께하는 개발 http://blog.novice.io 2014 Pycon Korea 구경 Pycon 2014 Getting Started Testing
  4. 4. 오늘오늘 발표의발표의 목적은목적은??
  5. 5. 테스트에테스트에 관심을관심을 가져보세요가져보세요 테스팅이테스팅이 처음이신처음이신 분들께분들께
  6. 6. 다른다른 사람은사람은 어떻게어떻게 테스트테스트 할까할까?? 테스팅을테스팅을 이미이미 ((잘잘)) 하고하고 계신계신 분들께분들께
  7. 7. 꼭꼭 테스트를테스트를 작성해야작성해야 하나요하나요??
  8. 8. 누군가는누군가는 여러분의여러분의 코드를코드를 테테 스트스트 해야해야 합니다합니다
  9. 9. 길게길게 보면보면 시간이시간이 절약절약 됩니다됩니다
  10. 10. 변경에변경에 대한대한 자신감을자신감을 얻으얻으 세요세요
  11. 11. 더더 나은나은 코드를코드를 만들어만들어 줍니다줍니다
  12. 12. PythonPython 은은 컴파일러가컴파일러가 없잖아요없잖아요
  13. 13. 어떤어떤 테스트를테스트를 작성해야작성해야 하나요하나요??
  14. 14. 수많은수많은 종류의종류의 테스트가테스트가 있습니다있습니다 Testing Types Installation testing Compatibility testing Smoke and sanity testing Regression testing Acceptance testing Alpha testing Beta testing Functional vs non- functional testing Destructive testing Software performance testing Usability testing Accessibility testing Security testing Internationalization and localization Development testing A/B testing Concurrent testing Conformance testing or type testing Testing levels Unit testing Integration testing Component interface testing System testing Operational Acceptance testing D Whitebox testing Blackbox testing Testing methods Static testing ynamic testing
  15. 15. 단위단위 테스트테스트 (Unit Test) 기능기능 테스트테스트 (Function Test) 개발자 뷰 함수 단위 Mock 을 사용 빠름 더 좋은 코드에 기여 사용자 뷰 요구사항 단위 Fixture 를 사용 느림 퇴근에 기여
  16. 16. 일단일단 하나만하나만 보여줘보여줘 봐요봐요
  17. 17. PortfolioPortfolio # portfolio.py from http://nedbatchelder.com/text/st.html#7 class Portfolio(object): """간단한 주식 포트폴리오""" def __init__(self): # stocks is a list of lists: # [[name, shares, price], ...] self.stocks = [] def buy(self, name, shares, price): """name 주식을 shares 만큼 주당 price 에 삽니다""" self.stocks.append([name, shares, price]) def cost(self): """이 포트폴리오의 총액은 얼마일까요?""" amt = 0.0 for name, shares, price in self.stocks: amt += shares * price return amt
  18. 18. 첫번째첫번째 테스트테스트 -- 쉘쉘 Python 3.4.0 (default, Jun 19 2015, 14:20:21) [GCC 4.8.2] on linux Type "help", "copyright", "credits" or "license" for more informati >>> from portfolio import Portfolio >>> p = Portfolio() >>> print(p.cost()) 0.0 >>> p.buy('Google', 100, 176.48) >>> p.cost() 17648.0 >>> p.buy('Yahoo', 100, 36.15) >>> p.cost() 21263.0 Good 테스트 했어요! Bad 다시 테스트 하려면? 직접 입력 그래서 잘 된건가?
  19. 19. 두번째두번째 테스트테스트 -- 기대값기대값 from portfolio import Portfolio p = Portfolio() print("Empty portfolio cost: %s, should be 0.0" % p.cost()) p.buy('Google', 100, 176.48) print("With 100 Google @ 176.48: %s, should be 17648.0" % p.cost()) p.buy('Yahoo', 100, 36.15) print("With 100 Yahoo @ 36.15: %s, should be 21263.0" % p.cost()) Good 테스트 했음 다시 테스트 할 수 있음 잘 된건지 확인 가능 Bad 눈으로 확인 해야 함 Empty portfolio cost: 0.0, should be 0.0 With 100 Google @ 176.48: 17648.0, should be 17648.0 With 100 Yahoo @ 36.15: 21263.0, should be 21263.0
  20. 20. 세번째세번째 테스트테스트 -- 결과결과 확인확인 from portfolio import Portfolio p = Portfolio() print("Empty portfolio cost: %s, should be 0.0" % p.cost()) p.buy('Google', 100, 176.48) assert p.cost() == 17649.0 # Failed print("With 100 Google @ 176.48: %s, should be 17648.0" % p.cost()) p.buy('Yahoo', 100, 36.15) assert p.cost() == 21263.0 print("With 100 Yahoo @ 36.15: %s, should be 21263.0" % p.cost()) Good 다시 테스트 할 수 있음 잘 된건지 자동으로 확인 가 능 Bad 왜 틀렸는지 알기 힘듬 두번째 테스트가 실행 되지 않음 Empty portfolio cost: 0.0, should be 0.0 Traceback (most recent call last): File "portfolio_test2.py", line 6, in <module> assert p.cost() == 17649.0 # Failed AssertionError
  21. 21. ((개발개발 시작시의시작시의 마음과마음과 달리달리)) 테스트를테스트를 지속하지지속하지 못하는못하는 이유이유??
  22. 22. 점점점점 복잡해복잡해 진다진다. (. (코드와코드와 함께함께)) 반복되는반복되는 노력이노력이 아깝다아깝다.. frameworkframework 의의 사용을사용을 고민해고민해 볼볼 때때!! 어떻게어떻게 테스트테스트 해야해야 할지할지 모른다모른다..
  23. 23. Unittest !Unittest ! (들어봤지만 친하지 않은 그대)
  24. 24. unittestunittest 파이썬파이썬 표준표준 라이브러리라이브러리 테스트테스트 자동화자동화 테스트테스트 구조화구조화 테스트테스트 결과결과 보고보고
  25. 25. TestCaseTestCase 독립적인독립적인 테스트테스트 단위단위
  26. 26. TestSuiteTestSuite TestCaseTestCase 의의 묶음묶음
  27. 27. TestRunnerTestRunner 테스트를테스트를 실제로실제로 수행하고수행하고 결과를결과를 레포트레포트 한다한다..
  28. 28. 테스트테스트 흐름흐름
  29. 29. First UnittestFirst Unittest # portfolio_test3.py import unittest from portfolio import Portfolio class PortfolioTest(unittest.TestCase): def test_google(self): p = Portfolio() p.buy("Google", 100, 176.48) self.assertEqual(17648.0, p.cost()) if __name__ == '__main__': unittest.main() $ python portfolio_test3.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
  30. 30. 테스트가테스트가 실패실패 했어요했어요!! - 실패하라고 테스트는 만드는 겁니다.
  31. 31. 테스트테스트 추가추가 # portfolio_test4.py import unittest from portfolio import Portfolio class PortfolioTestCase(unittest.TestCase): def test_google(self): p = Portfolio() p.buy("Goole", 100, 176.48) self.assertEqual(17648.0, p.cost()) def test_google_yahoo(self): p = Portfolio() p.buy("Google", 100, 176.48) p.buy("Yahoo", 100, 36.15) self.assertEqual(21264.0, p.cost()) # 21263.0 if __name__ == '__main__': unittest.main()
  32. 32. UnittestUnittest 실패실패 $ python portfolio_test4.py .F ====================================================================== FAIL: test_google_yahoo (__main__.PortfolioTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "portfolio_test4.py", line 15, in test_google_yahoo self.assertEqual(21264.0, p.cost()) AssertionError: 21264.0 != 21263.0 ---------------------------------------------------------------------- Ran 2 tests in 0.005s FAILED (failures=1) Good 테스트 실패가 다른 테스트에 영향을 미치지 않음 실패한 위치와 이유를 알 수 있음
  33. 33. 실패한실패한 테스트만테스트만 다시다시 돌리고돌리고 싶어요싶어요..
  34. 34. TestTest 고르기고르기 $ python portfolio_test4.py PortfolioTestCase.test_google_yahoo F ====================================================================== FAIL: test_google_yahoo (__main__.PortfolioTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "portfolio_test4.py", line 15, in test_google_yahoo self.assertEqual(21264.0, p.cost()) AssertionError: 21264.0 != 21263.0 ---------------------------------------------------------------------- Ran 1 test in 0.005s FAILED (failures=1) Good 원하는 테스트만 빠르게 실행 해 볼 수 있음 출력이 간단해짐
  35. 35. 테스트테스트 파일이파일이 늘어나면늘어나면 어어 떻게떻게 한꺼번에한꺼번에 실행실행 시키시키 죠죠??
  36. 36. 테스트테스트 한꺼번에한꺼번에 실행하기실행하기 $ python -m unittest discover --help Usage: python -m unittest discover [options] Options: ,,, -s START, --start-directory=START Directory to start discovery ('.' default) -p PATTERN, --pattern=PATTERN Pattern to match tests ('test*.py' default) -t TOP, --top-level-directory=TOP Top level directory of project (defaults to start directory) $ python -m unittest discover ---------------------------------------------------------------------- Ran 15 tests in 0.130s OK Good 복수개의 파일을 한꺼번에 테스트를 실행 할 수있음
  37. 37. ExceptionException 도도 테스트테스트 할할 수수 있나요있나요??
  38. 38. Portfolio -Portfolio - 타입타입 확인확인 class Portfolio(object): """간단한 주식 포트폴리오""" def __init__(self): # stocks is a list of lists: # [[name, shares, price], ...] self.stocks = [] def buy(self, name, shares, price): """name 주식을 shares 만큼 주당 price 에 삽니다""" self.stocks.append([name, shares, price]) if not isinstance(shares, int): raise Exception("shares must be an integer") def cost(self): """이 포트폴리오의 총액은 얼마일까요?""" amt = 0.0 for name, shares, price in self.stocks: amt += shares * price return amt
  39. 39. ExceptionException 을을 발생시키는발생시키는 테스트테스트 import unittest from portfolio import Portfolio class PortfolioTestCase(unittest.TestCase): def test_google(self): p = Portfolio() p.buy("Goole", "many", 176.48) self.assertEqual(17648.0, p.cost()) if __name__ == '__main__': unittest.main() $ python ./portfolio_test5.py E ====================================================================== ERROR: test_google (__main__.PortfolioTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "./portfolio_test5.py", line 8, in test_google p.buy("Goole", "many", 176.48) File "/home/leclipse/git/pycon-testing/unit_test/portfolio.py", line 14, in buy raise Exception("shares must be an integer") Exception: shares must be an integer ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (errors=1)
  40. 40. ExceptionException 을을 테스트테스트 import unittest from portfolio import Portfolio class PortfolioTestCase(unittest.TestCase): def test_google(self): p = Portfolio() with self.assertRaises(Exception) as context: p.buy("Goole", "many", 176.48) self.assertTrue("shares must be an integer", context.exception) if __name__ == '__main__': unittest.main() $ python ./portfolio_test6.py . ---------------------------------------------------------------------- Ran 1 test in 0.004s OK
  41. 41. TestCase.assert*TestCase.assert* 는는 어떤것들이어떤것들이 있나요있나요??
  42. 42. 적절한적절한 assertassert 의의 사용은사용은 테테 스트스트 코드를코드를 간단하게간단하게 합니합니 다다..
  43. 43. TestCase.assert*TestCase.assert* Unittest Assert Methods
  44. 44. 테스트마다테스트마다 반복되는반복되는 초기초기 화는화는 어떻게어떻게 관리하나요관리하나요??
  45. 45. import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose() self.widget = None def test_default_size(self): self.assertEqual(self.widget.size(), (50,50), 'incorrect default size') def test_resize(self): self.widget.resize(100,150) self.assertEqual(self.widget.size(), (100,150), 'wrong size after resize') setUp(), tearDown()setUp(), tearDown()
  46. 46. 테스트테스트 흐름흐름
  47. 47. 비슷한비슷한 테스트를테스트를 반복반복 하고하고 싶을싶을 때는때는??
  48. 48. 반복되는반복되는 테스트테스트 실행하기실행하기 import unittest class NumberTest(unittest.TestCase): def test_even(self): for i in range(0, 6): self.assertEqual(i % 2, 0) def test_even_with_subtest(self): for i in range(0, 6): with self.subTest(i=i): self.assertEqual(i % 2, 0) unittest.main() 0 부터 5 까지 짝수인지를 테스트 합니다.
  49. 49. SubtestSubtest 실행실행 결과결과 $ python ./subtest.py F ====================================================================== FAIL: test_even (__main__.NumberTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "./subtest.py", line 7, in test_even self.assertEqual(i % 2, 0) AssertionError: 1 != 0 ====================================================================== FAIL: test_even_with_subtest (__main__.NumberTest) (i=1) ---------------------------------------------------------------------- Traceback (most recent call last): File "./subtest.py", line 12, in test_even_with_subtest self.assertEqual(i % 2, 0) AssertionError: 1 != 0 ====================================================================== FAIL: test_even_with_subtest (__main__.NumberTest) (i=3) ---------------------------------------------------------------------- Traceback (most recent call last): File "./subtest.py", line 12, in test_even_with_subtest self.assertEqual(i % 2, 0) AssertionError: 1 != 0 ====================================================================== FAIL: test_even_with_subtest (__main__.NumberTest) (i=5) ---------------------------------------------------------------------- Traceback (most recent call last): File "./subtest.py", line 12, in test_even_with_subtest self.assertEqual(i % 2, 0) AssertionError: 1 != 0 ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=4) Good 중단 되지 않고 모두 테 스트 변경 되는 값을 확인 할 수 있다. python 3.4 에 추가 되었음
  50. 50. UnittestUnittest 복잡하군요복잡하군요
  51. 51. # test_runner.py def do_test(): for testcase in testsuite: for test_method in testcase.test_methods: try: testcase.setUp() except: [record error] else: try: test_method() except AssertionError: [record failure] except: [record error] else: [record success] finally: try: testcase.tearDown() except: [record error] print(do_test()) 다시다시 보는보는 unittestunittest 구조구조
  52. 52. 의존성이의존성이 있어요있어요.. 어떻게어떻게 테스트테스트 하죠하죠??
  53. 53. 의존성이의존성이 있는있는 코드코드 def get_username(user_id): user = db.user.query(user_id = user_id) return user.name def delete_user(user_id): return db.user.delete(user_id = user_id) DBDB 가가 있어야있어야 테스트테스트 할할 수수 있음있음 DBDB 에에 테스트전테스트전 데이터를데이터를 넣어넣어 주어야주어야 함함 CICI 에서는에서는 어떻게어떻게 실행실행 할까할까??
  54. 54. 테스트는테스트는 여기까지여기까지 하는하는 것것 이이 좋겠다좋겠다.. ......가가 아니고아니고
  55. 55. mockmock 을을 사용합시다사용합시다..
  56. 56. Mock?!Mock?!
  57. 57. MockMock
  58. 58. unittest.mockunittest.mock 파이썬 표준 라이브러리 (3.3 부터) 이전 버젼은 pip install mock python object 들을 동적으로 대체하고 사용 결과를 확인 하기 위한 다양한 기능들을 제공 의존성이 있는것들을 실제로 실행시키지 말고 호출 여 부, 인터페이스만 확인 하자
  59. 59. Monkey PatchMonkey Patch >>> class Class(): ... def add(self, x, y): ... return x + y ... >>> inst = Class() >>> def not_exactly_add(self, x, y): ... return x * y ... >>> Class.add = not_exactly_add >>> inst.add(3, 3) 9 런타임에 클래스, 함수등을 변경하는 것
  60. 60. mockmock 사용사용 예예 >>> from unittest.mock import MagicMock >>> thing = ProductionClass() >>> thing.method = MagicMock(return_value=3) >>> thing.method(3, 4, 5, key='value') 3 >>> thing.method.assert_called_with(3, 4, 5, key='value') thing.method 가 monkey patch 되었음 이를 테스트에 어떻게 활용 할까?
  61. 61. test rmtest rm # Rm.py import os def rm(filename): os.remove(filename) # test_rm.py from Rm import rm class RmTestCase(unittest.TestCase): tmpfilepath = os.path.join(tempfile.gettempdir(), 'temp-testfile') def setUp(self): with open(self.tmpfilepath, 'w') as f: f.write('Delete me!') def tearDown(self): if os.path.isfile(self.tmpfilepath): os.remove(self.tmpfilepath) def test_rm(self): rm(self.tmpfilepath) self.assertFalse(os.path.isfile(self.tmpfilepath), 'Failed to remove the file')
  62. 62. 첫번째첫번째 Mock testMock test import os.path import tempfile import unittest from unittest import mock from Rm import rm class RmTestCase(unittest.TestCase): @mock.patch('Rm.os') def test_rm(self, mock_os): rm('/tmp/tmpfile') mock_os.remove.assert_called_with('/tmp/tmpfile') if __name__ == '__main__': unittest.main() Good setUp, tearDown 이 없어졌음 실제로 os.remove 이 호출되지 않았음 os.remove 가 호출되었는지는 확인 했음 test_rm Rm.rm os.remove mock_os. remove
  63. 63. 어떻게어떻게 된걸까된걸까?? # Rm.py import os def rm(filename): print(os.remove) os.remove(filename) $ python ./test_rm.py <built-in function remove> . ---------------------------------------------------------------------- Ran 1 test in 0.007s OK $ python ./mock_rm.py <MagicMock name='os.remove' id='139901238735592'> . ---------------------------------------------------------------------- Ran 1 test in 0.002s OK
  64. 64. MockMock 대상이대상이 되는되는 것것 시간이 오래 걸리는 것 값이 변하는 것 상태가 유지 되는 것 (다른 테스트에 영향을 주는 것) 시스템 콜 네트워크로 연결된 것 준비하기 복잡한 것
  65. 65. Realworld exampleRealworld example class SimpleFacebook(object): def __init__(self, oauth_token): self.graph = facebook.GraphAPI(oauth_token) def post_message(self, message): self.graph.put_object('me', 'feed', message=message) class SimpleFacebookTestCase(unittest.TestCase): @mock.patch.object(facebook.GraphAPI, 'put_object', autospect=True) def test_post_message(self, mock_put_object): sf = SimpleFacebook('fake oauth token') sf.post_message('Hello World!') mock_put_object.assert_called_with('me', 'feed', message='Hello World!') facebook 이 다운되어도 내 테스트는 실패 하지 않는다.
  66. 66. Mock -Mock - 조건조건 확인확인 # 24시간이 지난 경우에만 삭제 합니다. def rm(filename): file_modified = datetime.datetime.fromtimestamp(os.path.getmtime(filename if datetime.datetime.now() - file_modified > datetime.timedelta(hours=24) os.remove(filename) class RmTestCase(unittest.TestCase): @mock.patch('__main__.os') def test_rm(self, mock_os): mock_os.path.getmtime.return_value = time.time() rm('/tmp/tmpfile') self.assertFalse(mock_os.remove.called) mock_os.path.getmtime.return_value = time.time() - 86400*2 rm('/tmp/tmpfile') mock_os.remove.assert_called_with('/tmp/tmpfile') 보통 분기를 따라가면서 테스트 하기는 쉽지 않음
  67. 67. MockMock 예외예외 # Rm.py class MyError(Exception): pass def rm(filename): try: os.remove(filename) except FileNotFoundError: raise MyError class RmTestCase(unittest.TestCase): @mock.patch.object(os, 'remove', side_effect=FileNotFoundError) def test_rm_without_file(self, mock_remove): with self.assertRaises(MyError) as context: rm('not_exist_file') Exception 이 발생되는 경우를 만들지 않아도 됨
  68. 68. 그래도그래도 합쳐서합쳐서 테스트는테스트는 해해 봐야봐야 하잖아요하잖아요??
  69. 69. Integration TestIntegration Test 테스트 환경이 동작하지 않아요. 프로덕션 환경이랑 테스트 환경이 다른 것 같은데요? 저 지금 테스트 환경에 배포 해도 돼요? 제가 지금 테스트 하고 있으니, 다른 분은 나중에 테스트 해주세 요. 다른 모듈,서비스 등을 붙여서 그 관계에서의 문제점을 확인하는 과정
  70. 70. 나만 쓰는 프로덕션 환경과 동일한 테스 트 환경이 있으면 좋겠다.
  71. 71. Redis ClientRedis Client # client.py import sys import redis class Client(): def __init__(self): self.conn = redis.StrictRedis("redis") def set(self, key, value): self.conn.set(key, value) def get(self, key): return self.conn.get(key) 실제 Redis 와 연결했을 때 잘 동작하는지 확인이 필요하다. # test_client.py import unittest from client import Client class ClientTestCase(unittest.TestCase): def test_with_redis(self): client = Client() client.set('tomato', 2) self.assertEqual(2, int(client.get('tomato')))
  72. 72. 나만 쓰는 프로덕션 환경과 동일한 테스 트 환경이 있으면 좋겠다. Host Host
  73. 73. DockerDocker Linux Container 를 이용 가벼운 VM 이라고 생각하자 Host 와 격리시킬 수 있음 https://www.docker.com/
  74. 74. DockerfileDockerfile FROM python:3.4 MAINTAINER lee ho sung RUN pip install redis RUN mkdir /code ADD . /code WORKDIR /code CMD python -m unittest discover Docker 이미지를 정의한다.
  75. 75. Docker-composeDocker-compose Python! 프로젝트 복수개의 Docker Container 를 관리 https://github.com/docker/compose
  76. 76. docker-compose.ymldocker-compose.yml client: build: . links: - redis redis: image: redis ports: - 6379:6379 192.168.0.2192.168.0.1 /etc/hosts redis 192.168.0.2
  77. 77. docker-compose updocker-compose up Good Localhost 에서 모든 테스트가 가능 Host 에 영향 없음 CI 에서도 실행 가능 $ docker-compose up redis_1 | 1:M 28 Aug 06:05:53.613 # Server started, Redis version 3.0.3 redis_1 | 1:M 28 Aug 06:05:53.613 # WARNING overcommit_memory is set to 0! Background save may fai redis_1 | 1:M 28 Aug 06:05:53.613 # WARNING you have Transparent Huge Pages (THP) support enabled redis_1 | 1:M 28 Aug 06:05:53.613 # WARNING: The TCP backlog setting of 511 cannot be enforced bec redis_1 | 1:M 28 Aug 06:05:53.614 * DB loaded from disk: 0.000 seconds redis_1 | 1:M 28 Aug 06:05:53.614 * The server is now ready to accept connections on port 6379 client_1 | . client_1 | ---------------------------------------------------------------------- client_1 | Ran 1 test in 1.003s client_1 | client_1 | OK integrationtest_client_1 exited with code 0 Gracefully stopping... (press Ctrl+C again to force) Stopping integrationtest_redis_1... done $
  78. 78. UIUI가가 잘잘 동작하는지동작하는지 어떻게어떻게 테스트테스트 하죠하죠??
  79. 79. 그냥그냥 손으로손으로 합니다합니다 ...... 가가 아니고아니고
  80. 80. SeleniumSelenium 웹 브라우저 자동화 툴/라이브러리 https://github.com/seleniumhq/selenium from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.python.org") assert "Python" in driver.title
  81. 81. 사람과사람과 동일한동일한 방식으로방식으로!!
  82. 82. Test Pycon 2015Test Pycon 2015 import unittest from selenium import webdriver from selenium.webdriver.common.keys import Keys class PyconUserTestCase(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() def test_log_in_pycon_2015(self): driver = self.driver driver.get("http://www.pycon.kr/2015/login") # US1 : 사용자는 Pycon Korea 2015 페이지 제목을 볼 수 있습니다. self.assertIn("PyCon Korea 2015", driver.title) # US2 : 사용자는 로그인을 할 수 있습니다. # 로그인 하면 "One-time login token ..." 메시지를 볼 수 있습니다. elem = driver.find_element_by_id("id_email") elem.send_keys("email-me@gmail.com") elem.send_keys(Keys.RETURN) self.assertIn("One-time login token url was sent to your mail", driver.page_source) def tearDown(self): self.driver.close() if __name__ == "__main__": unittest.main(warnings='ignore')
  83. 83. 어디까지어디까지 테스트테스트 해야해야 하나하나 요요??
  84. 84. 경우에경우에 따라따라 많이많이 다릅니다다릅니다.. 최소 : 테스트 케이스가 메인 로직을 검증 한다. 최대 : 개발 시간의 2배 이상을 쓰지 않는다. 이 사이에서 가장 가성비 높은 지점을 찾아내는 것이 좋은 개발자!
  85. 85. 테스트는테스트는 언제언제 실행실행 하나요하나요??
  86. 86. 문제는문제는 빠르게빠르게 찾을찾을 수록수록 좋습니다좋습니다.. 가능한가능한 자주자주 실행실행 합니다합니다.. 개발할 때 commit 할 때 push 할 때 CI 서버에서 오픈 소스를 받자마자
  87. 87. 좋은좋은 테스트란테스트란 무엇인가요무엇인가요??
  88. 88. 한번에한번에 하나만하나만 테스트테스트 합니다합니다 실패가실패가 명확해야명확해야 합니다합니다 빠르게빠르게 테스트테스트 되어야되어야 합니다합니다 중복중복 되지되지 않습니다않습니다 자동화자동화 되어야되어야 합니다합니다 독립적이어야독립적이어야 합니다합니다 ((다른것의다른것의 영향을영향을 받지받지 않습니다않습니다))
  89. 89. 오늘오늘 무슨무슨 이야기를이야기를 했죠했죠??
  90. 90. 테스트를테스트를 작성작성 하는하는 이유이유 간단한간단한 테스트테스트 unittestunittest 의의 사용사용 의존성이의존성이 있을있을 때때 mockmock 사용하기사용하기 dockerdocker 사용해서사용해서 통합통합 테스트테스트 하기하기 seleniumselenium을을 사용해서사용해서 웹웹 테스트테스트 하기하기
  91. 91. 테스트는 힘들지만 가치 있는 일입니다 지금 시작하세요!
  92. 92. 참고참고 자료자료
  93. 93. Pycon 2014 Getting Started Testing Python Testing Style Guide An Introduction to mocking in Python Test Driven Development with Python
  94. 94. 감사합니다감사합니다..

×