SlideShare une entreprise Scribd logo
1  sur  30
Télécharger pour lire hors ligne
테스트가 뭐예요?
ODK Media LTE 2020.4.23 / 정경업
테스트가 뭐예요?
어디서 쓰나요?
내 프로젝트에는 어떻게 넣나요?
1
정경업(별명: 파이)
ODK 입사 2주년(아직 청정수)
ODK Lighting Talk Engineering (강제)진행자(발표 브로커)
다른 발표들
Media IO가 뭐죠? (ODK Media 2020.1.10)
신입에서 CTO까지, 야근하지 않는 웹개발 (PyCon Korea 2017)
Django로 쇼핑몰 만들자 (PyCon Korea 2016)
ODK Media LTE 2020.4.23 / 정경업
ODK Media / Backend Lead Senior Software Engineer
2
먼 옛날
석우징: 짜잔, 이렇게 짜면 됩니다.
정경업: 뭐 하러 그렇게까지 만들어요???
* 실화
미안하다 이거 보여주려고 어그로끌었다.. 나루토 사스케 싸움수준 ㄹㅇ실화냐? 진짜 세
계관최강자들의 싸움이다.. 그찐따같던 나루토가 맞나? 진짜 나루토는 전설이다..
ODK Media LTE 2020.4.23 / 정경업
석우징(갓우징>가두징) 본좌께서
Django 스터디 그룹에 코딩 도장을 운영하며
직접 테스트 케이스를 짜며 시범을 보이시던 때
3
테스트가 뭐고 어떻게 쓰는지
경험을 공유합니다.
ODK Media LTE 2020.4.23 / 정경업
Python/Django 프로젝트에서
4
우리는 코드를 짜고 확인합니다.
ODK Media LTE 2020.4.23 / 정경업
5
if 될까():
print('왜 되지?')
else:
print('왜 안돼?')
2번 돌려보면 되겠네
ODK Media LTE 2020.4.23 / 정경업
조건이 단순하면?
6
if 될까() == 'A':
print('A')
if 될까() == 'B':
print('B')
if 될까() == 'C':
print('C')
if 될까() == 'D':
print('D')
코드 작성 -> 실행 -> print 확인 X 확인할 숫자
더 많아지면..?
ODK Media LTE 2020.4.23 / 정경업
조건이 복잡하면?
7
Program - Episode 모델이 있을 때
Episode의 재생을 허용하는 국가인지 확인하는 함수
episode.is_allowed_country('US')
요구사항
기본적으로 Program으로 전체적인 허용 국가를 정하고요
특정 Episode별 허용 국가를 달리하고 싶어요
아무것도 설정된게 없으면 다 나오고요
ODK Media LTE 2020.4.23 / 정경업
하나 짜봅시다.
8
class AllowedCountriesModel(models.Model):
allowed_countries = ArrayField(
models.CharField(max_length=2, blank=True, default=list,
help_text='Allowed Countries(ISO-3166 Alpha-2 code)')
class Meta:
abstract = True
class Program(AllowedCountriesModel, models.Model):
# 생략했지만 뭔가 많은 필드들 1
class Episode(AllowedCountriesModel, models.Model):
program = models.ForeignKey(Program)
# 생략했지만 뭔가 많은 필드들 2
def is_allowed_country(self, country: str) -> bool:
if self.allowed_countries:
return country in self.allowed_countries
if self.program.allowed_countries:
return country in self.program.allowed_countries
return True
ODK Media LTE 2020.4.23 / 정경업
9
잘 짰나 확인을 해봐야죠?
ODK Media LTE 2020.4.23 / 정경업
어떻게?
10
1. Shell
>>> from content.models import Program, Episode
>>> program = Program.objects.create(allowed_countries=['KR'])
>>> episode = Episode.objects.create(allowed_countries=[])
>>> episode.is_allowed_country('US')
>>> False
>>> episode.allowed_countries=['US']
>>> episode.is_allowed_country('US')
>>> True
>>> # To be coutinue...!
가능한 시나리오를 모두 열심히 타이핑
실패하면 코드를 바꾸고 처음부터 다시(혹은 복사 붙여넣기)
ODK Media LTE 2020.4.23 / 정경업
11
2. Admin
Program 생성 - Episode 생성 - is_allowed_country 확인 x 시나리오 수
ODK Media LTE 2020.4.23 / 정경업
12
3. Test code
from parameterized import parameterized
from django.test import TestCase
class TestEpisodeModel(TestCase):
@parameterized.expand([
# (program_countries, episode_countries, expected)
(['KR', 'CA'], [], False),
(['US', 'CA'], ['CA', 'KR'], False),
(['CA', 'KR'], ['US', 'KR'], True),
([], ['US', 'KR'], True),
([], [], True),
])
def test_is_allowed_country(self, pr_countries, ep_countries, expected):
program = ProgramFactory(allowed_countries=pr_countries)
episode = EpisodeFactory(program=program, allowed_countries=ep_countries)
self.assertEqual(episode.is_allowed_country('US'), expected)
> python manage.py test
ODK Media LTE 2020.4.23 / 정경업
13
잘 짜여진 테스트 코드가 있으면
테스트 코드는 거의 그대로 읽을 수 있음
높은 가독성 - 요구사항을 놓치기가 더 힘듦
잘못짜는게 더 힘듦
잘못짜서 수정시 테스트를 실행만 해보면 바로 확인
코드 수정 - 확인에 걸리는 시간 감소
자주 코드를 고칠 수 있음 - 실력 향상!
ODK Media LTE 2020.4.23 / 정경업
14
코드를 믿을 수 있음 - 장애 덜 남 - 남는 시간에 기능 업데이트
라이브러리 업데이트 쉬움 - 최신 코드 유지
코드 리펙토링에 부담이 적음 - 자주함 - 실력 향상!
코드를 고쳤을때 영향이 가는 모든 곳을 직접 확인 해야함
힘드니까 업데이트 안함 - 낡아가는 코드 - 점점 안(못) 고침
뭘 고치면 장애도 같이 남 - 장애 대응 - 실력 향상은 언제?
ODK Media LTE 2020.4.23 / 정경업
모든 코드에 테스트가 있다면?
없으면?
15
테스트는 작은 단위 일수록 짜기 쉽다
분할 정복 실현 - 한 함수에서 하는 일은 하나만
의존성이 낮을수록 짜기 쉽다
의존성 관리 - 불필요한 의존을 제거
자연스럽게 클린 아키텍처에 가까워 질 수 있다
기능 추가 쉬워짐
요구사항에 유연하게 대응 가능
ODK Media LTE 2020.4.23 / 정경업
테스트 잘 짜려고 하다보면?
16
고칠 용기를 주는 코드 - 최대한 명확하고 짧고 간결
테스트 코드 간 의존성 없게 - 멱등성*, 사이드 이펙트 X, 병렬 실행(속도)
얼마나? 최대한, 가능한 모든 코드에 테스트 코드 작성
언제? 코드를 먼저 짜든, 테스트를 먼저 짜든, 상황에 따라
* 멱등성: 수학이나 전산학에서 연산의 한 성질을 나타내는 것으로, 연산을 여러 번 적용
하더라도 결과가 달라지지 않는 성질을 의미
ODK Media LTE 2020.4.23 / 정경업
어떤 테스트 코드가 잘 짠걸까?
17
구체적으로 어떻게?
ODK Media LTE 2020.4.23 / 정경업
18
Pycharm 같은 IDE 추천
레벨이 딸리면 장비라도 좋아야 사냥을 할 수 있다.
장비빨은 현질이 짱임
ODK Media LTE 2020.4.23 / 정경업
일단 쉽게 돌릴 수 있는 환경을 만들자
19
테스트 코드에 필요한 구조화 된 값(모델 등)을 손쉽게 생성 / 대체
반복적인 테스트 코드의 가독성을 극적으로 향상
Factory Boy https://factoryboy.readthedocs.io/
데이터 구조 정의를 쉽게 할 수 있음
유연하고 풍부한 기능들
여러 ORM 지원 (Django 등)
ODK Media LTE 2020.4.23 / 정경업
Fixtures tool
20
# Factory Boy 같은게 없다면? 매번 모든 필드 할당
category = Category.objects.create(**{
'service_name': 'foo',
'slug': 'drama',
'kind': CategoryType.CATEGORY.value
'title': '드라마'
})
# Factory Boy를 쓰면? 상황에 따라 필요한 필드만 할당
category = CategoryFactory(slug='drama')
# Factory 정의
class CategoryFactory(factory.django.DjangoModelFactory):
class Meta:
model = Category
service_name = factory.fuzzy.FuzzyChoice(choices=['foo', 'bar'])
slug = factory.Sequence(lambda n: f'{fake.slug()[:10]}-{n}')
kind = CategoryType.CATEGORY.value
title = factory.Faker('text', max_nb_chars=5)
ODK Media LTE 2020.4.23 / 정경업
21
Django test plus https://django-test-plus.readthedocs.io/
Django 기본 TestCase 보다
풍부한 Shortcut을 제공하여 코드량을 줄이고 가독성도 높임
from django.core.urlresolvers import reverse
def test_status(self):
response = self.client.get(reverse('my-url-name'))
self.assertEqual(response.status_code, 200)
def test_better_status(self):
response = self.get('my-url-name')
self.assert_http_200_ok(response)
ODK Media LTE 2020.4.23 / 정경업
TestCase Shortcuts
22
자주 쓰는 Shortcut을 확장하여 사용해보기
class TestCategoryAPIView(TestCase):
def test_create(self):
self.post_check(201, 'api:admin:category-list', data={
'service_name': 'foo', 'slug': 'drama', 'kind': 'category',
'title_ko': '드라마', 'title_en': 'Drama'
})
class TestCase(PlusTestCase):
def request_check(self, method, status_code, url, *args, **kwargs):
if method != 'get' and 'data' in kwargs:
try:
kwargs['data'] = json.dumps(kwargs.get('data', {}))
kwargs['extra'] = {'content_type': 'application/json'}
except TypeError:
pass
response = self.request(method, url, *args, **kwargs)
self.assertEqual(status_code, response.status_code)
return response
def post_check(self, status_code, url, *args, **kwargs):
return self.request_check('post', status_code, url, *args, **kwargs)
ODK Media LTE 2020.4.23 / 정경업
23
회원 가입 API의 Test code 예시
class TestUserAPIView(TestCase):
def test_sign_up(self):
username = 'new@test.com'
response = self.post_check(201, 'api:v1:user-sign-up', data={
'username': username,
'password': 'password',
'confirm_password': 'password',
'code': generate_verification_code(username, 'email')
})
detail = response.json()['detail']
user = User.objects.get(username=username)
self.assertEqual(user.username, detail['user']['username'])
self.assertTrue(user.is_active)
self.assertEqual(Token.objects.get(user=user).key, detail['token'])
# welcome email
sent = mail.outbox[0]
self.assertEqual(sent.subject, 'Welcome!')
self.assertIn(user.username, sent.variables)
ODK Media LTE 2020.4.23 / 정경업
24
parameterized https://pypi.org/project/parameterized/
테스트 함수를 데코레이터로 정의된 내용대로 반복 실행
class TestCategoryAdminAPIViewPermissions(TestCase):
@parameterized.expand([(None,), 'user']) # 한번 정의 해서 두번 확인
def test_permissions(self, user):
if user:
self.login(getattr(self, user))
category = CategoryFactory()
self.get_check(403, 'api:admin:category-list')
self.post_check(403, 'api:admin:category-list')
self.get_check(403, 'api:admin:category-detail', pk=category.id)
self.put_check(403, 'api:admin:category-detail', pk=category.id)
self.patch_check(403, 'api:admin:category-detail', pk=category.id)
self.delete_check(403, 'api:admin:category-detail', pk=category.id)
ODK Media LTE 2020.4.23 / 정경업
반복적인 테스트 줄이기
25
unittest.subTest
https://docs.python.org/3/library/unittest.html#distinguishing-test-
iterations-using-subtests
반복문 안의 assert를 각각 다른 테스트로 추적해 줌
class TestCategoryAPIView(TestCase):
def test_detail(self):
drama = CategoryFactory(slug='drama')
self.get_check(200, 'api:admin:category-detail', pk=drama.id)
data = self.last_response.json()
for field in ['service_name', 'slug', 'kind', 'title_ko', 'title_en']:
with self.subTest(field=field):
# field가 각각 다르게 적혀있는 테스트로 인식함
self.assertEqual(data[field], getattr(drama, field))
ODK Media LTE 2020.4.23 / 정경업
26
unittest.mock https://docs.python.org/3/library/unittest.mock.html
class TestVideoAPIView(TestCase):
# 외부에 있는 Video API를 호출하는 함수를 mock
@patch('api.common.video_api.requests.get')
def test_list(self, mock_get):
# 응답값을 상황에 맞춰 가짜로 생성
mock_get.return_value = FakeResponse(status_code=200, content={'count': 3})
# 목록이 뜨는지 확인
response = self.get_check(200, 'api:admin:video-list')
self.assertEqual(response.json()['count'], 3)
* mock: 모조품, 가짜
ODK Media LTE 2020.4.23 / 정경업
외부 서비스 연동된 코드 테스트
27
freezegun https://pypi.org/project/freezegun/
from freezegun import freeze_time
class TestBannerAPIViewPick(TestCase):
def test_lives(self):
# 오늘(지금) 시작된 배너가
banner = BannerFactory(start=timezone.now())
# 시간을 어제로 돌려서 없는지 확인
yesterday = timezone.now() - timedelta(days=1)
with freeze_time(yesterday):
response = self.get_check(200, 'api:v1:banner-lives')
ODK Media LTE 2020.4.23 / 정경업
테스트 시간 바꾸기
28
결론
테스트 코드 안 짤 이유가 없다
짤 때는 가독성을 신경 쓰자
여러 툴/라이브러리 잘 쓰자
ODK Media LTE 2020.4.23 / 정경업
테스트 코드로 빠르게 실력을 올리고
안정된 서비스로 개발 시간 확보하자
29
끝
ODK Media LTE 2020.4.23 / 정경업
질문 받습니다
30

Contenu connexe

Tendances

Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Kyoung Up Jung
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 
Django in Production
Django in ProductionDjango in Production
Django in ProductionHyun-woo Park
 
회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemyJc Kim
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1성일 한
 
Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기성일 한
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2성일 한
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축Youngil Cho
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4성일 한
 
챗봇 시작해보기
챗봇 시작해보기챗봇 시작해보기
챗봇 시작해보기성일 한
 
장고로 웹서비스 만들기 기초
장고로 웹서비스 만들기   기초장고로 웹서비스 만들기   기초
장고로 웹서비스 만들기 기초Kwangyoun Jung
 
201803 파이썬 세미나
201803 파이썬 세미나201803 파이썬 세미나
201803 파이썬 세미나JeongHwan Kim
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는Taegon Kim
 
[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery정석 양
 
JavaSript Template Engine
JavaSript Template EngineJavaSript Template Engine
JavaSript Template EngineOhgyun Ahn
 
Python 웹 프로그래밍
Python 웹 프로그래밍Python 웹 프로그래밍
Python 웹 프로그래밍용 최
 
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Youngil Cho
 

Tendances (20)

플라스크 템플릿
플라스크 템플릿플라스크 템플릿
플라스크 템플릿
 
Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 
Django in Production
Django in ProductionDjango in Production
Django in Production
 
Light Tutorial Django
Light Tutorial DjangoLight Tutorial Django
Light Tutorial Django
 
회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1
 
Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4
 
챗봇 시작해보기
챗봇 시작해보기챗봇 시작해보기
챗봇 시작해보기
 
Handlebars
HandlebarsHandlebars
Handlebars
 
장고로 웹서비스 만들기 기초
장고로 웹서비스 만들기   기초장고로 웹서비스 만들기   기초
장고로 웹서비스 만들기 기초
 
201803 파이썬 세미나
201803 파이썬 세미나201803 파이썬 세미나
201803 파이썬 세미나
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는
 
[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery
 
JavaSript Template Engine
JavaSript Template EngineJavaSript Template Engine
JavaSript Template Engine
 
Python 웹 프로그래밍
Python 웹 프로그래밍Python 웹 프로그래밍
Python 웹 프로그래밍
 
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
 

Similaire à 테스트가 뭐예요?

Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기hanbeom Park
 
20201121 코드 삼분지계
20201121 코드 삼분지계20201121 코드 삼분지계
20201121 코드 삼분지계Chiwon Song
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Testbeom kyun choi
 
코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011Esun Kim
 
TestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolTestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolhyunae lee
 
TestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolTestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolhyunae lee
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018Kenneth Ceyer
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010Ryan Park
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10Ryan Park
 
Google Protocol buffer
Google Protocol bufferGoogle Protocol buffer
Google Protocol bufferknight1128
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스Sungik Kim
 
The roadtocodecraft
The roadtocodecraftThe roadtocodecraft
The roadtocodecraftbbongcsu
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙Hyosang Hong
 
Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Hyosang Hong
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기현철 조
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례SangIn Choung
 
Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발지수 윤
 
오픈 스펙을 대상으로 한 테스트설계사례
오픈 스펙을 대상으로 한 테스트설계사례오픈 스펙을 대상으로 한 테스트설계사례
오픈 스펙을 대상으로 한 테스트설계사례SangIn Choung
 

Similaire à 테스트가 뭐예요? (20)

Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기
 
20201121 코드 삼분지계
20201121 코드 삼분지계20201121 코드 삼분지계
20201121 코드 삼분지계
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011
 
TestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolTestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing tool
 
TestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing toolTestExplorer 소개 - Android application GUI testing tool
TestExplorer 소개 - Android application GUI testing tool
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
Google Protocol buffer
Google Protocol bufferGoogle Protocol buffer
Google Protocol buffer
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스
 
The roadtocodecraft
The roadtocodecraftThe roadtocodecraft
The roadtocodecraft
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙
 
Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례
 
Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발
 
오픈 스펙을 대상으로 한 테스트설계사례
오픈 스펙을 대상으로 한 테스트설계사례오픈 스펙을 대상으로 한 테스트설계사례
오픈 스펙을 대상으로 한 테스트설계사례
 
Custom assert
Custom assertCustom assert
Custom assert
 

Plus de Kyoung Up Jung

Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdf
Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdfDjango 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdf
Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdfKyoung Up Jung
 
OK, 계획대로 되고 있어?
OK, 계획대로 되고 있어?OK, 계획대로 되고 있어?
OK, 계획대로 되고 있어?Kyoung Up Jung
 
Django를 배우다, Django로 배우다.
Django를 배우다, Django로 배우다.Django를 배우다, Django로 배우다.
Django를 배우다, Django로 배우다.Kyoung Up Jung
 
어른스럽게 일하기
어른스럽게 일하기어른스럽게 일하기
어른스럽게 일하기Kyoung Up Jung
 
신입에서 CTO까지, 야근하지 않는 웹개발
신입에서 CTO까지, 야근하지 않는 웹개발신입에서 CTO까지, 야근하지 않는 웹개발
신입에서 CTO까지, 야근하지 않는 웹개발Kyoung Up Jung
 
웹 개발, 왜 어려운가?
웹 개발, 왜 어려운가?웹 개발, 왜 어려운가?
웹 개발, 왜 어려운가?Kyoung Up Jung
 
Django ORM 왜 어렵게 느껴질까?
Django ORM 왜 어렵게 느껴질까?Django ORM 왜 어렵게 느껴질까?
Django ORM 왜 어렵게 느껴질까?Kyoung Up Jung
 
뭔지 모르지만 발표
뭔지 모르지만 발표뭔지 모르지만 발표
뭔지 모르지만 발표Kyoung Up Jung
 
Django개발은 PyCharm에서
Django개발은 PyCharm에서Django개발은 PyCharm에서
Django개발은 PyCharm에서Kyoung Up Jung
 

Plus de Kyoung Up Jung (10)

Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdf
Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdfDjango 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdf
Django 봄은 다시 온다 - Django와 함께 좋은 웹서비스 코드 만들기.pdf
 
NRISE에서 3개월
NRISE에서 3개월NRISE에서 3개월
NRISE에서 3개월
 
OK, 계획대로 되고 있어?
OK, 계획대로 되고 있어?OK, 계획대로 되고 있어?
OK, 계획대로 되고 있어?
 
Django를 배우다, Django로 배우다.
Django를 배우다, Django로 배우다.Django를 배우다, Django로 배우다.
Django를 배우다, Django로 배우다.
 
어른스럽게 일하기
어른스럽게 일하기어른스럽게 일하기
어른스럽게 일하기
 
신입에서 CTO까지, 야근하지 않는 웹개발
신입에서 CTO까지, 야근하지 않는 웹개발신입에서 CTO까지, 야근하지 않는 웹개발
신입에서 CTO까지, 야근하지 않는 웹개발
 
웹 개발, 왜 어려운가?
웹 개발, 왜 어려운가?웹 개발, 왜 어려운가?
웹 개발, 왜 어려운가?
 
Django ORM 왜 어렵게 느껴질까?
Django ORM 왜 어렵게 느껴질까?Django ORM 왜 어렵게 느껴질까?
Django ORM 왜 어렵게 느껴질까?
 
뭔지 모르지만 발표
뭔지 모르지만 발표뭔지 모르지만 발표
뭔지 모르지만 발표
 
Django개발은 PyCharm에서
Django개발은 PyCharm에서Django개발은 PyCharm에서
Django개발은 PyCharm에서
 

테스트가 뭐예요?

  • 1. 테스트가 뭐예요? ODK Media LTE 2020.4.23 / 정경업 테스트가 뭐예요? 어디서 쓰나요? 내 프로젝트에는 어떻게 넣나요? 1
  • 2. 정경업(별명: 파이) ODK 입사 2주년(아직 청정수) ODK Lighting Talk Engineering (강제)진행자(발표 브로커) 다른 발표들 Media IO가 뭐죠? (ODK Media 2020.1.10) 신입에서 CTO까지, 야근하지 않는 웹개발 (PyCon Korea 2017) Django로 쇼핑몰 만들자 (PyCon Korea 2016) ODK Media LTE 2020.4.23 / 정경업 ODK Media / Backend Lead Senior Software Engineer 2
  • 3. 먼 옛날 석우징: 짜잔, 이렇게 짜면 됩니다. 정경업: 뭐 하러 그렇게까지 만들어요??? * 실화 미안하다 이거 보여주려고 어그로끌었다.. 나루토 사스케 싸움수준 ㄹㅇ실화냐? 진짜 세 계관최강자들의 싸움이다.. 그찐따같던 나루토가 맞나? 진짜 나루토는 전설이다.. ODK Media LTE 2020.4.23 / 정경업 석우징(갓우징>가두징) 본좌께서 Django 스터디 그룹에 코딩 도장을 운영하며 직접 테스트 케이스를 짜며 시범을 보이시던 때 3
  • 4. 테스트가 뭐고 어떻게 쓰는지 경험을 공유합니다. ODK Media LTE 2020.4.23 / 정경업 Python/Django 프로젝트에서 4
  • 5. 우리는 코드를 짜고 확인합니다. ODK Media LTE 2020.4.23 / 정경업 5
  • 6. if 될까(): print('왜 되지?') else: print('왜 안돼?') 2번 돌려보면 되겠네 ODK Media LTE 2020.4.23 / 정경업 조건이 단순하면? 6
  • 7. if 될까() == 'A': print('A') if 될까() == 'B': print('B') if 될까() == 'C': print('C') if 될까() == 'D': print('D') 코드 작성 -> 실행 -> print 확인 X 확인할 숫자 더 많아지면..? ODK Media LTE 2020.4.23 / 정경업 조건이 복잡하면? 7
  • 8. Program - Episode 모델이 있을 때 Episode의 재생을 허용하는 국가인지 확인하는 함수 episode.is_allowed_country('US') 요구사항 기본적으로 Program으로 전체적인 허용 국가를 정하고요 특정 Episode별 허용 국가를 달리하고 싶어요 아무것도 설정된게 없으면 다 나오고요 ODK Media LTE 2020.4.23 / 정경업 하나 짜봅시다. 8
  • 9. class AllowedCountriesModel(models.Model): allowed_countries = ArrayField( models.CharField(max_length=2, blank=True, default=list, help_text='Allowed Countries(ISO-3166 Alpha-2 code)') class Meta: abstract = True class Program(AllowedCountriesModel, models.Model): # 생략했지만 뭔가 많은 필드들 1 class Episode(AllowedCountriesModel, models.Model): program = models.ForeignKey(Program) # 생략했지만 뭔가 많은 필드들 2 def is_allowed_country(self, country: str) -> bool: if self.allowed_countries: return country in self.allowed_countries if self.program.allowed_countries: return country in self.program.allowed_countries return True ODK Media LTE 2020.4.23 / 정경업 9
  • 10. 잘 짰나 확인을 해봐야죠? ODK Media LTE 2020.4.23 / 정경업 어떻게? 10
  • 11. 1. Shell >>> from content.models import Program, Episode >>> program = Program.objects.create(allowed_countries=['KR']) >>> episode = Episode.objects.create(allowed_countries=[]) >>> episode.is_allowed_country('US') >>> False >>> episode.allowed_countries=['US'] >>> episode.is_allowed_country('US') >>> True >>> # To be coutinue...! 가능한 시나리오를 모두 열심히 타이핑 실패하면 코드를 바꾸고 처음부터 다시(혹은 복사 붙여넣기) ODK Media LTE 2020.4.23 / 정경업 11
  • 12. 2. Admin Program 생성 - Episode 생성 - is_allowed_country 확인 x 시나리오 수 ODK Media LTE 2020.4.23 / 정경업 12
  • 13. 3. Test code from parameterized import parameterized from django.test import TestCase class TestEpisodeModel(TestCase): @parameterized.expand([ # (program_countries, episode_countries, expected) (['KR', 'CA'], [], False), (['US', 'CA'], ['CA', 'KR'], False), (['CA', 'KR'], ['US', 'KR'], True), ([], ['US', 'KR'], True), ([], [], True), ]) def test_is_allowed_country(self, pr_countries, ep_countries, expected): program = ProgramFactory(allowed_countries=pr_countries) episode = EpisodeFactory(program=program, allowed_countries=ep_countries) self.assertEqual(episode.is_allowed_country('US'), expected) > python manage.py test ODK Media LTE 2020.4.23 / 정경업 13
  • 14. 잘 짜여진 테스트 코드가 있으면 테스트 코드는 거의 그대로 읽을 수 있음 높은 가독성 - 요구사항을 놓치기가 더 힘듦 잘못짜는게 더 힘듦 잘못짜서 수정시 테스트를 실행만 해보면 바로 확인 코드 수정 - 확인에 걸리는 시간 감소 자주 코드를 고칠 수 있음 - 실력 향상! ODK Media LTE 2020.4.23 / 정경업 14
  • 15. 코드를 믿을 수 있음 - 장애 덜 남 - 남는 시간에 기능 업데이트 라이브러리 업데이트 쉬움 - 최신 코드 유지 코드 리펙토링에 부담이 적음 - 자주함 - 실력 향상! 코드를 고쳤을때 영향이 가는 모든 곳을 직접 확인 해야함 힘드니까 업데이트 안함 - 낡아가는 코드 - 점점 안(못) 고침 뭘 고치면 장애도 같이 남 - 장애 대응 - 실력 향상은 언제? ODK Media LTE 2020.4.23 / 정경업 모든 코드에 테스트가 있다면? 없으면? 15
  • 16. 테스트는 작은 단위 일수록 짜기 쉽다 분할 정복 실현 - 한 함수에서 하는 일은 하나만 의존성이 낮을수록 짜기 쉽다 의존성 관리 - 불필요한 의존을 제거 자연스럽게 클린 아키텍처에 가까워 질 수 있다 기능 추가 쉬워짐 요구사항에 유연하게 대응 가능 ODK Media LTE 2020.4.23 / 정경업 테스트 잘 짜려고 하다보면? 16
  • 17. 고칠 용기를 주는 코드 - 최대한 명확하고 짧고 간결 테스트 코드 간 의존성 없게 - 멱등성*, 사이드 이펙트 X, 병렬 실행(속도) 얼마나? 최대한, 가능한 모든 코드에 테스트 코드 작성 언제? 코드를 먼저 짜든, 테스트를 먼저 짜든, 상황에 따라 * 멱등성: 수학이나 전산학에서 연산의 한 성질을 나타내는 것으로, 연산을 여러 번 적용 하더라도 결과가 달라지지 않는 성질을 의미 ODK Media LTE 2020.4.23 / 정경업 어떤 테스트 코드가 잘 짠걸까? 17
  • 18. 구체적으로 어떻게? ODK Media LTE 2020.4.23 / 정경업 18
  • 19. Pycharm 같은 IDE 추천 레벨이 딸리면 장비라도 좋아야 사냥을 할 수 있다. 장비빨은 현질이 짱임 ODK Media LTE 2020.4.23 / 정경업 일단 쉽게 돌릴 수 있는 환경을 만들자 19
  • 20. 테스트 코드에 필요한 구조화 된 값(모델 등)을 손쉽게 생성 / 대체 반복적인 테스트 코드의 가독성을 극적으로 향상 Factory Boy https://factoryboy.readthedocs.io/ 데이터 구조 정의를 쉽게 할 수 있음 유연하고 풍부한 기능들 여러 ORM 지원 (Django 등) ODK Media LTE 2020.4.23 / 정경업 Fixtures tool 20
  • 21. # Factory Boy 같은게 없다면? 매번 모든 필드 할당 category = Category.objects.create(**{ 'service_name': 'foo', 'slug': 'drama', 'kind': CategoryType.CATEGORY.value 'title': '드라마' }) # Factory Boy를 쓰면? 상황에 따라 필요한 필드만 할당 category = CategoryFactory(slug='drama') # Factory 정의 class CategoryFactory(factory.django.DjangoModelFactory): class Meta: model = Category service_name = factory.fuzzy.FuzzyChoice(choices=['foo', 'bar']) slug = factory.Sequence(lambda n: f'{fake.slug()[:10]}-{n}') kind = CategoryType.CATEGORY.value title = factory.Faker('text', max_nb_chars=5) ODK Media LTE 2020.4.23 / 정경업 21
  • 22. Django test plus https://django-test-plus.readthedocs.io/ Django 기본 TestCase 보다 풍부한 Shortcut을 제공하여 코드량을 줄이고 가독성도 높임 from django.core.urlresolvers import reverse def test_status(self): response = self.client.get(reverse('my-url-name')) self.assertEqual(response.status_code, 200) def test_better_status(self): response = self.get('my-url-name') self.assert_http_200_ok(response) ODK Media LTE 2020.4.23 / 정경업 TestCase Shortcuts 22
  • 23. 자주 쓰는 Shortcut을 확장하여 사용해보기 class TestCategoryAPIView(TestCase): def test_create(self): self.post_check(201, 'api:admin:category-list', data={ 'service_name': 'foo', 'slug': 'drama', 'kind': 'category', 'title_ko': '드라마', 'title_en': 'Drama' }) class TestCase(PlusTestCase): def request_check(self, method, status_code, url, *args, **kwargs): if method != 'get' and 'data' in kwargs: try: kwargs['data'] = json.dumps(kwargs.get('data', {})) kwargs['extra'] = {'content_type': 'application/json'} except TypeError: pass response = self.request(method, url, *args, **kwargs) self.assertEqual(status_code, response.status_code) return response def post_check(self, status_code, url, *args, **kwargs): return self.request_check('post', status_code, url, *args, **kwargs) ODK Media LTE 2020.4.23 / 정경업 23
  • 24. 회원 가입 API의 Test code 예시 class TestUserAPIView(TestCase): def test_sign_up(self): username = 'new@test.com' response = self.post_check(201, 'api:v1:user-sign-up', data={ 'username': username, 'password': 'password', 'confirm_password': 'password', 'code': generate_verification_code(username, 'email') }) detail = response.json()['detail'] user = User.objects.get(username=username) self.assertEqual(user.username, detail['user']['username']) self.assertTrue(user.is_active) self.assertEqual(Token.objects.get(user=user).key, detail['token']) # welcome email sent = mail.outbox[0] self.assertEqual(sent.subject, 'Welcome!') self.assertIn(user.username, sent.variables) ODK Media LTE 2020.4.23 / 정경업 24
  • 25. parameterized https://pypi.org/project/parameterized/ 테스트 함수를 데코레이터로 정의된 내용대로 반복 실행 class TestCategoryAdminAPIViewPermissions(TestCase): @parameterized.expand([(None,), 'user']) # 한번 정의 해서 두번 확인 def test_permissions(self, user): if user: self.login(getattr(self, user)) category = CategoryFactory() self.get_check(403, 'api:admin:category-list') self.post_check(403, 'api:admin:category-list') self.get_check(403, 'api:admin:category-detail', pk=category.id) self.put_check(403, 'api:admin:category-detail', pk=category.id) self.patch_check(403, 'api:admin:category-detail', pk=category.id) self.delete_check(403, 'api:admin:category-detail', pk=category.id) ODK Media LTE 2020.4.23 / 정경업 반복적인 테스트 줄이기 25
  • 26. unittest.subTest https://docs.python.org/3/library/unittest.html#distinguishing-test- iterations-using-subtests 반복문 안의 assert를 각각 다른 테스트로 추적해 줌 class TestCategoryAPIView(TestCase): def test_detail(self): drama = CategoryFactory(slug='drama') self.get_check(200, 'api:admin:category-detail', pk=drama.id) data = self.last_response.json() for field in ['service_name', 'slug', 'kind', 'title_ko', 'title_en']: with self.subTest(field=field): # field가 각각 다르게 적혀있는 테스트로 인식함 self.assertEqual(data[field], getattr(drama, field)) ODK Media LTE 2020.4.23 / 정경업 26
  • 27. unittest.mock https://docs.python.org/3/library/unittest.mock.html class TestVideoAPIView(TestCase): # 외부에 있는 Video API를 호출하는 함수를 mock @patch('api.common.video_api.requests.get') def test_list(self, mock_get): # 응답값을 상황에 맞춰 가짜로 생성 mock_get.return_value = FakeResponse(status_code=200, content={'count': 3}) # 목록이 뜨는지 확인 response = self.get_check(200, 'api:admin:video-list') self.assertEqual(response.json()['count'], 3) * mock: 모조품, 가짜 ODK Media LTE 2020.4.23 / 정경업 외부 서비스 연동된 코드 테스트 27
  • 28. freezegun https://pypi.org/project/freezegun/ from freezegun import freeze_time class TestBannerAPIViewPick(TestCase): def test_lives(self): # 오늘(지금) 시작된 배너가 banner = BannerFactory(start=timezone.now()) # 시간을 어제로 돌려서 없는지 확인 yesterday = timezone.now() - timedelta(days=1) with freeze_time(yesterday): response = self.get_check(200, 'api:v1:banner-lives') ODK Media LTE 2020.4.23 / 정경업 테스트 시간 바꾸기 28
  • 29. 결론 테스트 코드 안 짤 이유가 없다 짤 때는 가독성을 신경 쓰자 여러 툴/라이브러리 잘 쓰자 ODK Media LTE 2020.4.23 / 정경업 테스트 코드로 빠르게 실력을 올리고 안정된 서비스로 개발 시간 확보하자 29
  • 30. 끝 ODK Media LTE 2020.4.23 / 정경업 질문 받습니다 30