본문 바로가기
Development/Python

02. unittest 모듈과 기능 테스트

by 신군. 2019. 7. 28.
반응형

보통 블로그, 게시판, 투표 프로젝트 등으로 웹 프로그래밍을 설명한다.

그러나 본 교재에서 만드는 프로젝트는 일정관리(To-Do List) 프로그램이다. 이 프로그램은 충분히 웹 프로그래밍의 다양한 기법을 소개하면서도 복잡하지 않은 장점이 있다.

스토리보드(user story)

스토리보드 작성 및 기능 테스트 실행

기능 테스트는 사람마다 다르게 부르지만 그 의미는 같다.

기능 테스트(Functional Test) == 합격 테스트(Acceptance Test) == 종단 테스트(End-to-End Test)

기능 테스트의 시작점은 스토리보드 또는 시나리오(user story)를 작성하는 것이다.

스토리보드/시나리오는 프로그래밍 언어가 아니라 사람의 언어로 기술하는 것이다.

아래와 같이 function_tests.py 파일에 주석으로 기술하는 것으로 시작한다.

function_tests.py 파일

from selenium import webdriver browser = webdriver.Firefox() # 영애씨는 온라인 일정관리 앱을 알게 되어 홈페이지에 방문한다. browser.get('http://localhost:8000') # 홈페이지에 방문해 보니 제목이 "일정관리"인 것을 보고 홈페이지에 올바르게 방문한 것을 확인한다. assert '일정관리' in browser.title # 일정을 입력할 수 있는 페이지로 바로 이동한다. # 영애씨는 생일날 미역국을 끓이기 위해 텍스트박스에 "시장에서 미역 사기"를 입력한다. # 영애씨가 엔터를 입력하면 페이지를 새로고침해서 모든 일정 목록을 보여준다. # "1: 시장에서 미역 사기"가 첫 번째 할일로 일정 목록에서 보여진다. # 영애씨는 추가로 할일 텍스트박스에 입력할 수 있고 # "미역을 물에 불리기"라고 입력한다. # 다시 페이지를 새로고침해서 입력한 일정 두 가지 모두 목록에 표시한다. # 영애씨는 일정 목록이 사이트에 올바로 저장되었는지 궁금해서 # 고유 URL 생성을 확인한다. # 영애씨는 URL을 방문하고 일정 목록이 올바르게 있음을 확인한다. # 영애씨는 이제 만족하고 잠을 자러간다. browser.quit()

기능 테스트를 실행하면 Django 사이트의 기본 제목이 여전히 Welcome to Django이기 때문에 아래와 같이 에러가 발생한다.

$ python function_tests.py Traceback (most recent call last): File "function_tests.py", line 9, in <module> assert '일정관리' in browser.title AssertionError

위와 같이 assert 문을 작성하기보다 에러 메시지를 친절하게 확인할 수 있도록 아래와 같이 코드를 수정할 수 있다.

assert '일정관리' in browser.title, "Browser title was " + browser.title

그럼 아래와 같이 좀 더 친절하게 AssertionError를 확인할 수 있다.

$ python function_tests.py Traceback (most recent call last): File "function_tests.py", line 9, in <module> assert '일정관리' in browser.title, "Browser title was " + browser.title AssertionError: Browser title was Welcome to Django

파이썬 표준 라이브러리 unittest로 기능 테스트 작성 및 실행

앞서 작성한 기능 테스트를 좀 더 파이썬 표준 라이브러리 unittest로 좀 더 보기 좋게 클래스형으로 수정할 수 있다.

unittest 모듈의 사용 장점은 다음과 같다.

  • 파이썬 표준 라이브러리
  • 테스트 자동화
  • 테스트 구조화
  • 테스트 결과 보고

아래와 같이 unittest 라이브러리를 사용한 테스트 코드가 하는 일은 위에 스크립트형 코드와 같다.

function_tests.py 파일

import unittest from selenium import webdriver class NewVisitorTest(unittest.TestCase): def setUp(self): self.browser = webdriver.Firefox() def tearDown(self): self.browser.quit() def test_can_start_a_list_and_retrieve_it_later(self): # 영애씨는 온라인 일정관리 앱을 알게 되어 홈페이지에 방문한다. self.browser.get('http://localhost:8000') # 홈페이지에 방문해 보니 제목이 "일정관리"인 것을 보고 홈페이지에 올바르게 방문한 것을 확인한다. # assert '일정관리' in self.browser.title, "Browser title was " + self.browser.title self.assertIn('일정관리', self.browser.title) self.fail('테스트 종료') # 일정을 입력할 수 있는 페이지로 바로 이동한다. # 영애씨는 생일날 미역국을 끓이기 위해 텍스트박스에 "시장에서 미역 사기"를 입력한다. # 영애씨가 엔터를 입력하면 페이지를 새로고침해서 모든 일정 목록을 보여준다. # "1: 시장에서 미역 사기"가 첫 번째 할일로 일정 목록에서 보여진다. # 영애씨는 추가로 할일 텍스트박스에 입력할 수 있고 # "미역을 물에 불리기"라고 입력한다. # 다시 페이지를 새로고침해서 입력한 일정 두 가지 모두 목록에 표시한다. # 영애씨는 일정 목록이 사이트에 올바로 저장되었는지 궁금해서 # 고유 URL 생성을 확인한다. # 영애씨는 URL을 방문하고 일정 목록이 올바르게 있음을 확인한다. # 영애씨는 이제 만족하고 잠을 자러간다. if __name__ == '__main__': unittest.main(warnings='ignore')

모든 테스트를 unittest.TestCase 클래스를 상속하고 test_can_start_a_list_and_retrieve_it_later 같은 메소드를 추가하여 테스트를 시행한다. 이 때 메소드의 이름이 test_ 문자열로 시작하는 것들을 runner가 테스트로 시행한다.

setUp과 tearDown은 브라우저를 열고 접속한 닫는 부분을 처리한다.

assert 대신에 self.assertIn으로 헬퍼 함수를 활용할 수 있다. unittest 모듈은 assertEqual, assertTrue, assertFalse 같은 여러 헬퍼 함수를 제공한다.

self.fail 함수로 무조건적으로 테스트를 실패시킨다.

warnings='ignore' 파라미터로 너무 많은 경고 메시지가 출력되는 것을 방지한다.

기능 테스트를 아래와 같이 실행할 수 있고 역시 예상한대로 테스트는 실패한다. 별 차이는 없지만 메시지 출력 포맷이 좀 더 깔끔하다.

$ python function_tests.py F ====================================================================== FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisitorTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "function_tests.py", line 17, in test_can_start_a_list_and_retrieve_it_later assert '일정관리' in self.browser.title, "Browser title was " + self.browser.title AssertionError: Browser title was Welcome to Django ---------------------------------------------------------------------- Ran 1 test in 3.960s FAILED (failures=1)

커밋

로컬 저장소 상태를 확인하면 현재 function_tests.py 파일이 modified 상태임을 확인할 수 있다.

$ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: function_tests.py no changes added to commit (use "git add" and/or "git commit -a")

로컬 저장소의 변경 사항은 아래와 같이 확인할 수 있다.

$ git diff diff --git a/function_tests.py b/function_tests.py index df73c70..fb8c694 100644 --- a/function_tests.py +++ b/function_tests.py @@ -1,6 +1,41 @@ +import unittest + from selenium import webdriver -browser = webdriver.Firefox() -browser.get('http://localhost:8000') -assert 'Django' in browser.title +class NewVisitorTest(unittest.TestCase): + def setUp(self): + self.browser = webdriver.Firefox() + [... 생략 ...]

First FT specced out in comments, and now uses unittest 메시지로 커밋한다.

git commit -am "First FT spacced out in comments, and now uses unittest." [master cac9e1b] First FT spacced out in comments, and now uses unittest. 1 file changed, 42 insertions(+), 6 deletions(-) rewrite function_tests.py (75%)

-a 옵션은 변경된 파일을 추가하는 것과 동시에 커밋한다. -m 옵션은 커밋 메시지를 지정한다.

요약 및 의견

기능 테스트는 가장 먼저 사람의 언어로 그 내용을 작성한 후 예견되는 테스트 실패를 만든다. 그리고 그 테스트의 실패를 고쳐나가면서 개발을 진행한다.

스크립트 형태의 파이썬 코드를 파이썬 표준 라이브러리 모듈 unittest를 이용하면 함수형으로 구조화해 보기 좋게 만들 수 있다.

 

https://wikidocs.net/11058

반응형

'Development > Python' 카테고리의 다른 글

04. 테스트와 리팩토링  (0) 2019.07.28
03. 시작 페이지 단위 테스트  (0) 2019.07.28
01. Django 그리고 기능 테스트  (0) 2019.07.28
00. 준비하기  (0) 2019.07.28
Python 2.x 한글 인코딩 관련 정리  (0) 2019.07.28