Python 테스트 시작하기

  • View
    2.528

  • Download
    4

  • Category

    Software

Preview:

Citation preview

Python 테스트 시작하기이호성

에서 함께 보실 수 있습니다http://slides.com/hosunglee-1/deck/live

누구세요누구세요??

이호성이호성

EnswersDevOpsPython, Cloud Computing함께하는 개발 http://blog.novice.io

2014 Pycon Korea 구경

Pycon 2014 GettingStarted Testing

오늘오늘 발표의발표의 목적은목적은??

테스트에테스트에 관심을관심을 가져보세요가져보세요

테스팅이테스팅이 처음이신처음이신 분들께분들께

다른다른 사람은사람은 어떻게어떻게테스트테스트 할까할까??

테스팅을테스팅을 이미이미 ( (잘잘) ) 하고하고계신계신 분들께분들께

꼭꼭 테스트를테스트를작성해야작성해야 하나요하나요??

누군가는누군가는 여러분의여러분의 코드를코드를 테테스트스트 해야해야 합니다합니다

길게길게 보면보면 시간이시간이절약절약 됩니다됩니다

변경에변경에 대한대한 자신감을자신감을 얻으얻으세요세요

더더 나은나은 코드를코드를만들어만들어 줍니다줍니다

PythonPython 은은컴파일러가컴파일러가 없잖아요없잖아요

어떤어떤 테스트를테스트를작성해야작성해야 하나요하나요??

수많은수많은 종류의종류의 테스트가테스트가 있습니다있습니다

Testing TypesInstallation testingCompatibility testingSmoke and sanity testingRegression testingAcceptance testingAlpha testingBeta testingFunctional vs non-functional testingDestructive testingSoftware performancetestingUsability testingAccessibility testingSecurity testingInternationalization andlocalizationDevelopment testingA/B testingConcurrent testingConformance testing ortype testing

Testing levelsUnit testingIntegration testingComponent interface testingSystem testingOperational Acceptancetesting

DWhitebox testingBlackbox testing

Testing methodsStatic testing

ynamic testing

단위단위 테스트테스트(Unit Test)

기능기능 테스트테스트(Function Test)

개발자 뷰함수 단위Mock 을 사용 빠름더 좋은 코드에 기여

사용자 뷰요구사항 단위Fixture 를 사용느림퇴근에 기여

일단일단 하나만하나만보여줘보여줘 봐요봐요

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

첫번째첫번째 테스트테스트 - - 쉘쉘Python 3.4.0 (default, Jun 19 2015, 14:20:21) [GCC 4.8.2] on linuxType "help", "copyright", "credits" or "license" for more information.>>> 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다시 테스트 하려면? 직접 입력 그래서 잘 된건가?

두번째두번째 테스트테스트 - - 기대값기대값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.0With 100 Google @ 176.48: 17648.0, should be 17648.0With 100 Yahoo @ 36.15: 21263.0, should be 21263.0

세번째세번째 테스트테스트 - - 결과결과 확인확인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 # Failedprint("With 100 Google @ 176.48: %s, should be 17648.0" % p.cost())p.buy('Yahoo', 100, 36.15)assert p.cost() == 21263.0print("With 100 Yahoo @ 36.15: %s, should be 21263.0" % p.cost())

Good다시 테스트 할 수 있음잘 된건지 자동으로 확인 가능

Bad왜 틀렸는지 알기 힘듬두번째 테스트가 실행 되지않음

Empty portfolio cost: 0.0, should be 0.0Traceback (most recent call last): File "portfolio_test2.py", line 6, in <module> assert p.cost() == 17649.0 # FailedAssertionError

( (개발개발 시작시의시작시의 마음과마음과 달리달리))

테스트를테스트를 지속하지지속하지못하는못하는 이유이유??

점점점점 복잡해복잡해 진다진다. (. (코드와코드와 함께함께))

반복되는반복되는 노력이노력이 아깝다아깝다..

framework framework 의의 사용을사용을고민해고민해 볼볼 때때!!

어떻게어떻게 테스트테스트 해야해야 할지할지 모른다모른다..

Unittest ! Unittest ! (들어봤지만 친하지 않은 그대)

unittestunittest

파이썬파이썬 표준표준 라이브러리라이브러리

테스트테스트 자동화자동화

테스트테스트 구조화구조화

테스트테스트 결과결과 보고보고

TestCaseTestCase

독립적인독립적인 테스트테스트 단위단위

TestSuiteTestSuite

TestCase TestCase 의의 묶음묶음

TestRunnerTestRunner

테스트를테스트를 실제로실제로 수행하고수행하고 결과를결과를 레포트레포트 한다한다..

테스트테스트 흐름흐름

First UnittestFirst Unittest# portfolio_test3.pyimport unittestfrom 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

테스트가테스트가 실패실패 했어요했어요!!- 실패하라고 테스트는 만드는 겁니다.

테스트테스트 추가추가

# portfolio_test4.pyimport unittestfrom 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()

Unittest Unittest 실패실패$ 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테스트 실패가 다른 테스트에 영향을 미치지 않음실패한 위치와 이유를 알 수 있음

실패한실패한 테스트만테스트만다시다시 돌리고돌리고 싶어요싶어요..

Test Test 고르기고르기$ python portfolio_test4.py PortfolioTestCase.test_google_yahooF======================================================================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원하는 테스트만 빠르게 실행 해 볼 수 있음출력이 간단해짐

테스트테스트 파일이파일이 늘어나면늘어나면 어어떻게떻게 한꺼번에한꺼번에 실행실행 시키시키

죠죠??

테스트테스트 한꺼번에한꺼번에 실행하기실행하기$ python -m unittest discover --helpUsage: 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복수개의 파일을 한꺼번에 테스트를 실행 할 수있음

Exception Exception 도도 테스트테스트할할 수수 있나요있나요??

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

Exception Exception 을을 발생시키는발생시키는 테스트테스트import unittestfrom 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)

Exception Exception 을을 테스트테스트import unittestfrom 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

TestCase.assert* TestCase.assert* 는는어떤것들이어떤것들이 있나요있나요??

적절한적절한 assertassert 의의 사용은사용은 테테스트스트 코드를코드를 간단하게간단하게 합니합니

다다..

TestCase.assert*TestCase.assert*

Unittest Assert Methods

테스트마다테스트마다 반복되는반복되는 초기초기화는화는 어떻게어떻게관리하나요관리하나요? ?

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()

테스트테스트 흐름흐름

비슷한비슷한 테스트를테스트를반복반복 하고하고싶을싶을 때는때는??

반복되는반복되는 테스트테스트 실행하기실행하기

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 까지 짝수인지를 테스트 합니다.

Subtest Subtest 실행실행 결과결과$ 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 에 추가 되었음

Unittest Unittest 복잡하군요복잡하군요

# test_runner.pydef 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())

다시다시 보는보는 unittest unittest 구조구조

의존성이의존성이 있어요있어요..어떻게어떻게 테스트테스트 하죠하죠??

의존성이의존성이 있는있는 코드코드

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)

DB DB 가가 있어야있어야 테스트테스트 할할 수수 있음있음

DB DB 에에 테스트전테스트전 데이터를데이터를 넣어넣어 주어야주어야 함함

CI CI 에서는에서는 어떻게어떻게 실행실행 할까할까??

테스트는테스트는 여기까지여기까지 하는하는 것것이이 좋겠다좋겠다..

......가가 아니고아니고

mock mock을을 사용합시다사용합시다..

Mock?!Mock?!

MockMock

unittest.mockunittest.mock

파이썬 표준 라이브러리 (3.3 부터)이전 버젼은 pip install mockpython object 들을 동적으로 대체하고 사용 결과를 확인 하기 위한 다양한 기능들을 제공

의존성이 있는것들을 실제로 실행시키지 말고 호출 여부, 인터페이스만 확인 하자

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

런타임에 클래스, 함수등을 변경하는 것

mock mock 사용사용 예예>>> 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 되었음이를 테스트에 어떻게 활용 할까?

test rmtest rm# Rm.pyimport os

def rm(filename): os.remove(filename)

# test_rm.pyfrom 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')

첫번째첫번째 Mock test Mock testimport os.pathimport tempfileimport unittestfrom 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()

GoodsetUp, tearDown 이 없어졌음실제로 os.remove 이 호출되지 않았음os.remove 가 호출되었는지는 확인 했음

test_rm

Rm.rm

os.removemock_os.remove

어떻게어떻게 된걸까된걸까??# Rm.pyimport 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

Mock Mock 대상이대상이 되는되는 것것

시간이 오래 걸리는 것값이 변하는 것상태가 유지 되는 것 (다른 테스트에 영향을 주는 것)시스템 콜네트워크로 연결된 것준비하기 복잡한 것

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 이 다운되어도 내 테스트는 실패 하지 않는다.

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')

보통 분기를 따라가면서 테스트 하기는 쉽지 않음

Mock Mock 예외예외# Rm.pyclass 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 이 발생되는 경우를 만들지 않아도 됨

그래도그래도 합쳐서합쳐서 테스트는테스트는 해해봐야봐야 하잖아요하잖아요??

Integration TestIntegration Test

테스트 환경이 동작하지 않아요.프로덕션 환경이랑 테스트 환경이 다른 것 같은데요?저 지금 테스트 환경에 배포 해도 돼요?제가 지금 테스트 하고 있으니, 다른 분은 나중에 테스트 해주세요.

다른 모듈,서비스 등을 붙여서 그 관계에서의 문제점을 확인하는 과정

나만 쓰는 프로덕션 환경과 동일한 테스트 환경이 있으면 좋겠다.

Redis Client Redis Client# client.pyimport sysimport 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.pyimport unittestfrom 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')))

나만 쓰는 프로덕션 환경과 동일한 테스트 환경이 있으면 좋겠다.

Host Host

Docker Docker

Linux Container 를 이용가벼운 VM 이라고 생각하자Host 와 격리시킬 수 있음

https://www.docker.com/

Dockerfile Dockerfile

FROM python:3.4MAINTAINER lee ho sung

RUN pip install redis

RUN mkdir /codeADD . /codeWORKDIR /code

CMD python -m unittest discover

Docker 이미지를 정의한다.

Docker-compose Docker-compose

Python! 프로젝트복수개의 Docker Container 를 관리

https://github.com/docker/compose

docker-compose.ymldocker-compose.yml

client: build: . links: - redisredis: image: redis ports: - 6379:6379

192.168.0.2192.168.0.1

/etc/hosts redis 192.168.0.2

docker-compose updocker-compose up

GoodLocalhost 에서 모든 테스트가 가능Host 에 영향 없음CI 에서도 실행 가능

$ docker-compose up redis_1 | 1:M 28 Aug 06:05:53.613 # Server started, Redis version 3.0.3redis_1 | 1:M 28 Aug 06:05:53.613 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. 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 because /proc/sys/net/core/somaxconn redis_1 | 1:M 28 Aug 06:05:53.614 * DB loaded from disk: 0.000 secondsredis_1 | 1:M 28 Aug 06:05:53.614 * The server is now ready to accept connections on port 6379client_1 | .client_1 | ----------------------------------------------------------------------client_1 | Ran 1 test in 1.003sclient_1 | client_1 | OKintegrationtest_client_1 exited with code 0Gracefully stopping... (press Ctrl+C again to force)Stopping integrationtest_redis_1... done$

UI UI가가 잘잘 동작하는지동작하는지어떻게어떻게 테스트테스트 하죠하죠??

그냥그냥 손으로손으로 합니다합니다

... ... 가가 아니고아니고

Selenium Selenium

웹 브라우저 자동화 툴/라이브러리https://github.com/seleniumhq/selenium

from selenium import webdriver

driver = webdriver.Firefox()driver.get("http://www.python.org")assert "Python" in driver.title

사람과사람과 동일한동일한 방식으로방식으로!!

Test Pycon 2015Test Pycon 2015import unittestfrom selenium import webdriverfrom 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')

어디까지어디까지 테스트테스트 해야해야 하나하나요요??

경우에경우에 따라따라 많이많이 다릅니다다릅니다. .

최소 : 테스트 케이스가 메인 로직을 검증 한다.최대 : 개발 시간의 2배 이상을 쓰지 않는다.

이 사이에서 가장 가성비 높은 지점을 찾아내는 것이 좋은 개발자!

테스트는테스트는 언제언제 실행실행하나요하나요??

문제는문제는 빠르게빠르게 찾을찾을 수록수록 좋습니다좋습니다. . 가능한가능한 자주자주 실행실행 합니다합니다..

개발할 때 commit 할 때push 할 때CI 서버에서오픈 소스를 받자마자

좋은좋은 테스트란테스트란

무엇인가요무엇인가요??

한번에한번에 하나만하나만 테스트테스트 합니다합니다

실패가실패가 명확해야명확해야 합니다합니다

빠르게빠르게 테스트테스트 되어야되어야 합니다합니다

중복중복 되지되지 않습니다않습니다

자동화자동화 되어야되어야 합니다합니다

독립적이어야독립적이어야 합니다합니다((다른것의다른것의 영향을영향을 받지받지 않습니다않습니다))

오늘오늘 무슨무슨 이야기를이야기를 했죠했죠??

테스트를테스트를 작성작성 하는하는 이유이유

간단한간단한 테스트테스트

unittestunittest 의의 사용사용

의존성이의존성이 있을있을 때때 mockmock 사용하기사용하기

dockerdocker 사용해서사용해서 통합통합 테스트테스트 하기하기

seleniumselenium을을 사용해서사용해서 웹웹 테스트테스트 하기하기

테스트는 힘들지만가치 있는 일입니다

지금 시작하세요!

참고참고 자료자료

Pycon 2014 Getting Started TestingPython Testing Style GuideAn Introduction to mocking in PythonTest Driven Development with Python

감사합니다감사합니다..