여러분들께 조금이나 간단하게 마이리얼트립 제품 기획실 구성원을 소개 드리고 싶어서 구성원을 야구 포지션에 비유해 보겠습니다.

팀을 진두지휘하는 감독(PM), 감독을 보좌하며 전술을 구상하는 수석코치(Designer), 가장 먼저 상대 타자를 맞이하는 선발 투수(Front-End Dev & Mobile Dev), 선발 투수 다음으로 마운드에 등판하는 중간 계투(Back-End Dev), 경기를 마무리 짓기 위해 마지막으로 등판하는 마무리 투수(QA)가 있습니다.

네, 그렇습니다. 이번 이야기는 마이리얼트립에서 마무리 투수를 맡고 있는 ‘QA’ 이야기를 해보려 합니다.








Speed or Perfection

시작부터 뜬금없지만 여러분의 시간은 소중하니 지루한 소개는 생략할게요. 우선 마이리얼트립은 무슨 일이든 빠르게 시도하는 것을 지향합니다. 고민보다는 빠르게 실행하고 빠른 시도를 통해 다양한 성공과 실패를 경험하자는 뜻이 담겨있죠. QA 관점으로 실패(버그로 인한 사용자 경험 저해)는 고객중심 서비스에서 전혀 가치 있는 일이 아닙니다. 하지만 우리는 안타깝게도 매번 ‘완벽하진 않지만 빠르게?’ or ‘빠르진 않지만 완벽하게?’를 선택해야 하는 상황에 놓이곤 합니다. 예시를 한번 들어보겠습니다.


“코드 한 줄 수정했어요. 수정한 부분만 확인하고 바로 내보내시죠.”

회사 매출과 직결된 이슈이기 때문에 긴급하게 서비스 배포가 진행되어야 하는 상황입니다. 저도 다급한 상황임을 인지하고 있습니다만.. 수정된 부분만 확인하고 배포하기엔 자꾸만 Side-Effect 발생이 마음에 걸립니다. 우선 Side-Effect에 대해 말씀드리기 전 레거시 코드에 대해 간단하게나마 알아볼 필요가 있습니다.

레거시 코드란 유산이라는 뜻을 가진 Legacy와 Code가 합쳐진 합성어로 타인이 작성해 놓은 오래된 개발 코드로 이해하시면 편합니다. 오랜 시간을 거쳐 서비스가 성장하는 과정 속 개발 코드 또한 점점 방대해져 갑니다. 시간이 지나 퇴사자들이 생겨나면서 자연스럽게 퇴사자들이 작성했던 개발 코드, 즉 레거시 코드가 생겨납니다. 

이러한 레거시 코드는 작성자 이외에 다른 사람이 개발 의도를 파악하기 쉽지 않기 때문에, 레거시 코드를 지우거나 수정했을 때 프로그램에 어떠한 영향을 미칠지는 그 누구도 확신할 수 없게 됩니다. 그렇게 아무 곳에도 쓰이지 않을 법한 A 코드를 삭제했더니 숨어있던 B 코드에서 남모르게 A 코드를 사용하고 있는 상황이 발생하면서 오류가 발생합니다.








Regression Test를 통한 테스트 커버리지 확보

속도감 있게 서비스 배포를 진행하고자 한다면 보다 안정적인 서비스를 제공하기 위한 안전장치가 필요합니다. 우리는 Regression Test(회귀 테스트) 기법을 통해 빠르게 배포되는 일정 속 최대한의 테스트 커버리지를 확보활 수 있습니다.

Regression Test란 개발자가 수정한 소스 코드가 다른 부분에 영향을 미치지 않는지 테스트하여 소스 코드의 수정이 또 다른 오류를 발생시키지 않도록 검증하는 테스트 기법입니다. 또한 Test Case의 분량이 30분~1시간 내외이기 때문에 테스트에 소요되는 시간도 매우 짧은 편입니다. 검증 마지막 단계에서 Regression Test까지 진행한다면 배포되는 서비스의 Side-Effect 이슈에 대한 갈증을 해소할 수 있을 겁니다.


검증 방식에 정답은 없습니다. 수많은 테스트 기법이 존재하듯이 다양한 테스트 기법을 활용해 사용자들이 더욱 건강한 서비스를 사용할 수 있도록 노력하는 것만큼 가치 있는 일이 있을까요?










테스트 자동화를 통한 리소스 확보

어느 곳이든 소규모 QA는 항상 바쁩니다. 개발팀의 경우 각 파트마다 담당하는 플랫폼이 분산되어 있기 때문에 각자가 맡은 작업에만 집중하면 됩니다. 그러나 이 모든 작업들이 끝이 나면 산발적으로 QA에게 넘어오게 되죠. (이곳은 마치 옥천 hub..?) 적은 인원으로 업무를 골고루 분배하기 위해선 리소스 관리는 필수입니다. 


Regression Test는 서비스 검증의 마지막 단계에서 진행되어야 하는 고정적이고 반복적인 업무입니다. 아무리 30분~1시간 분량의 빠르게 진행되는 테스트라고 해도 서비스 배포가 다수의 플랫폼에서 동시에 진행되어야 한다면? 검증이 필요한 플랫폼마다 여러 차례 Regression Test를 수행한다고 가정했을 때 하루에 주어진 업무 시간의 거의 절반을 차지하게 됩니다. Regression Test는 분명 테스트 커버리지를 높여주고 속도감 있는 배포를 위한 테스트인데, 이런.. 양날의 검이 되어버렸습니다. 이때 등장해야 하는 것이 바로 테스트 자동화입니다.

테스트 자동화란 무엇일까요? 이름만 들어보면 ‘자동화’라는 단어 때문에 기계 혼자서 알아서 짠! 하고 테스트하는 것처럼 들리는데요. 기본적으로 테스트 자동화는 테스트 커버리지를 높여주는 목적으로 사용됩니다. 그 밖에도 사람이 수행하는 단순하고 반복적인 QA 업무를 소프트웨어의 도움을 받아 일관성 있는 테스트를 도와주는 테스트용 소프트웨어입니다. 그렇기 때문에 사람이 직접 테스트 코드를 작성하여 프로그램이 대신 테스트를 수행하는 것으로 이해하시면 됩니다.

이제 저희도 개발의 힘을 빌려봅시다. 혹시라도 프로그래밍 언어를 다뤄야 한다고 해서 겁먹을 필요는 없습니다. 개발자분들처럼 서버를 구축하고 앱을 개발하고, 이런것들은 잠시 넣어두세요. 우리는 테스트 자동화에 필요한 기본적인 지식들만 배워나가면 됩니다.









Selenium을 활용한 테스트 자동화

테스트 자동화에는 다양한 소프트웨어 프레임 워크가 있지만 그중 Selenium을 선택하였습니다. Selenium은 가장 대중적인 프레임 워크로써 참고할 만한 다양한 예제와 문제 해결 방안을 찾기에 용이하며, 다양한 서비스를 제공하지만 모두 오픈소스라는 장점이 있습니다. 또한 Python을 가뭄에 콩 나듯 공부해봤던 저에게 Selenium은 Java, Python, C#, Ruby 등 다양한 언어로 테스트 코드 작성이 가능하기 때문에 Selenium을 선택하게 되었습니다.

테스트 자동화가 실행되는 동안 다른 업무에 집중할 수 있고 그에 따른 리소스 확보가 가능하니 프로덕트의 변경점에 대한 테스트 코드의 유지 보수만 이루어진다면 얼마든지 QA 리소스 확보의 큰 도움이 될 수 있습니다.


                                       











Selenium Grid를 활용한 Parallel Test

Selenium에서 제공하는 Selenium GRID를 활용한다면 웹 브라우저, 모바일 앱의 Parallel Test가 가능하기 때문에, 다양한 웹 브라우저(IE, Chrome, FireFox 등) 테스트와 안드로이드 디바이스의 제조사(Samsung, LG, Google 등) 또는 OS 버전 별 다수의 플랫폼에서 동일한 테스트를 여러 환경에서 동시에 테스트가 가능하니 4번의 테스트를 한차례에 진행한다면 4배 빠른 테스트가 이루어지는 셈이죠.

Selenium Grid : Hub & Node 개념








Jenkins CI 빌드 스케줄러 활용한 서비스 모니터링

조금 더 응용해서 테스트 자동화와 Jenkins CI 빌드 스케줄러를 통해 주요 기능의 대한 서비스 모니터링도 가능합니다. 테스트 스크립트를 원하는 시간대를 설정하여 주기적으로 테스트 결과에 대한 내용을 Slack Bot으로 받아볼 수 있습니다.

Jenkins CI 슬랙 알림 연동 예시

테스트 자동화를 통해 Regression Test의 테스트 소요시간을 줄이면서, 다수의 플랫폼에서 반복적인 Regression Test 수행의 리소스 낭비를 Selenium GRID를 통해서 해결할 수 있습니다.
참고로 테스트 자동화는 ‘절대’ 마법이 아닙니다. 귀찮고 반복적인 일이 싫은 사람을 위한 도구일 뿐이지 그 이상 그 이하도 아닙니다. 언제나 그렇듯 사람이 꽃보다 아름답습니다.



우선 Jenkins 란?


오픈소스 CI (Continuous Integration) Tool 로 작업자들이 작업한 내용을 정기적으로 통합하여 관리하는걸 이야기한다.


CI Tool 의 활용 사례로는 분산 빌드 / 지속적인 빌드 및 테스트 자동화, 배포 자동화 / 테스트 보고서 작성 등이 있다.


유닛 테스트와 통합 테스트들의 정기적인 실행을 도와주어 오류 발생 시 실시간으로 메일이나 메신져로 리포팅이 가능하다.



bash 에 아래 명령어를 입력하여 Homebrew 를 설치한다.


$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"



Homebrew 설치가 완료되면 Homebrew 를 통해 Java 를 설치한다.

(JAVA 9 버전 부터 Jenkins 설치에 문제가 있어 JAVA 8 버전을 설치한다.)


$ brew tap caskroom/versions


$ brew cask install java8



핵심인 Jenkins 도 설치한다.


$ brew install jenkins


이제 젠킨스를 백그라운드 서비스로 구동하기 위해 다음 명령을 실행한다.


$ brew services start jenkins



젠킨스 설치, 실행도 완료하였으니 접속해보자.


http://localhost:8080/ <- 브라우저에 주소를 입력하면 젠킨스 설정 화면이 나타난다.




설치 후 최초 접속을 하면 위와 같이 인증 화면이 나온다.


cat 명령어를 통해 젠킨스를 설치하는 계정의 홈 디렉토리의 경로 > 초기 비밀번호를 확인하여 Password 를 입력해준다.


$ cat /Users/[계정명]/.jenkins/secrets/initialAdminPassword


추천 플러그인을 자동 선택하여 설치할 건지 원하는 것만 골라 설치한 건지 선택할 수 있다.


Install Suggested Plugins 클릭하여 젠킨스에서 추천해주는 플러그인을 설치한다.




여러가지 플러그인을 자동으로 설치하는데 간혹 네트워크 환경으로 설치가 실패되는 경우, Retry 를 선택하여 처음 부터 다시 설치해준다.




플러그인이 모두 정상적으로 설치 되면 관리자 계정을 만드는 페이지가 나타난다.


입력하는 정보를 모두 입력하고 계정을 생성해주자.




로그인까지 완료 후 아래 화면이 나타나면 젠킨스 설치하기 성공이다!




추가로 젠킨스 설치가 완료 된 컴퓨터는 계속 작동 시켜두고 나는 다른 PC 에서 젠킨스 서버에 접속하도록 설정 해보겠다.


먼저 아래 명령어를 bash 에 차례대로 입력한다.


$ cd /usr/local/opt/jenkins


vi 편집기에서 plist 파일에 httpListenAddress 주소를 0.0.0.0 으로 변경해준다.


$ vi homebrew.mxcl.jenkins.plist


편집 후 젠킨스 서버를 재시작한다.


$ brew services restart jenkins



이제 젠킨스가 설치된 PC 의 https://xxx.xxx.xxx.xx/8080 접속 시 동일 네트워크망의 다른 피씨에서도 젠킨스 서버 접속이 가능해졌다.



다음은 젠킨스에서 코드 관리 도구인 bitbucket 을 통해 자동화 코드를 가져오고 스케줄링을 통해 빌드를 발생시키는 방법을 포스팅하겠다.


끝! 💦



참고 : https://www.hooni.net/xe/study/79186#prettyPhoto

Python Unittest, 쉽게 말해 단위 테스트로 파이썬에서 제공하는 표준 라이브러리다.


Unittest 표준 라이브러리를 이용해 테스트 코드를 작성하자



우선 테스트 코드를 작성하려면 unittest.TestCase 를 상속받는 하위 클래스를 만든다.


from selenium import webdriver
import unittest


class mainTest(unittest.TestCase):
pass



다음 setUp(self) / runTest(self) / tearDown(self) 이렇게 세 개의 메소드를 오버라이드한다.


from selenium import webdriver
import unittest



class mainTest(unittest.TestCase):

def setUp(self):


def runTest(self):


def tearDown(self):


setUp(self) : 테스트 하기 전에 필요한 설정으로 여러 테스트 케이스가 공유하는 설정을 위해 사용 (브라우저 웹 드라이버 설치 등)


runTest(self) : 테스트 코드를 작성하는 부분, assertion, exception 등이 발생하면 tearDown(self) 을 실행


tearDown(self) : runTest(self) 종료 후 실행되는 메서드



https://chromedriver.storage.googleapis.com/index.html 로 이동하여 LATEST_RELEASE 파일을 열어본다.




뒤로 돌아가 방금 적혀있는 버전명과 같은 폴더로 접속하여 본인 운영체제와 맞는 chromedriver 를 다운 받는다. (필자는 mac OS)



앞으로 브라우저 드라이버의 경우 여러개의 드라이버를 사용할 수 있으니 따로 drivers 디렉토리를 생성하여 관리한다.


chromedriver 경로 : {본인 테스트 자동화 프로젝트}/drivers/chromedriver



setUp(self) 메소드에 class attribute 인 chromeDriver / driver / wait 을 만들어준다.


from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import unittest
import os
import time

PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)


class mainTest(unittest.TestCase):

def setUp(self):
self.chromeDriver = PATH('../drivers/chromedriver')
self.driver = webdriver.Chrome(executable_path=self.chromeDriver)
self.wait = WebDriverWait(self.driver, 5)

def runTest(self):


def tearDown(self):



준비는 다 끝났고 간단하게 웹 페이지 하나 띄워 볼 건데 driver.get 을 사용할 때는 반드시 http 프로토콜을 입력해줘야 똑바로 동작한다.


unittest.TestCase 의 구조는 setup > running > finish 구조 이며 finish 절인 def tearDown 에는 반드시 테스트 종료 후 수행할 동작을 명시해야 한다.


from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import unittest
import os
import time

PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)


class mainTest(unittest.TestCase):

def setUp(self):
self.chromeDriver = PATH('../drivers/chromedriver')
self.driver = webdriver.Chrome(executable_path=self.chromeDriver)
self.wait = WebDriverWait(self.driver, 5)

def runTest(self):
url = "http://gorillaz.tistory.com/"

self.driver.get(url)
time.sleep(5)

def tearDown(self):
self.driver.quit()



코드는 썼는데 실행을 해야지..이전 게시글에서 만들었던 test_suite.py 을 열어 아래와 같이 작성해준다.


간단하다, unittest.TestCase 를 상속받는 하위 클래스 mainTest 를 import 시켜준다.


from wwwauto.test1 import mainTest


이제 터미널에 아래 명령어를 입력해본다.


$ pytest -v -s wwwauto/



내가 pytest 를 사용하는 이유는...


test_suite 에 클래스를 모아놓고 순서대로 실행하려고 ㅎ





참고 : http://springs-thursday.iptime.org/?p=130

파이썬으로 개발을 하다보면 항상 복수의 패키지를 설치 혹은 사용하게 된다.


그런데 이걸 한꺼번에 설치한다면 해당 패키지는 그 컴퓨터에서 사용하는 모든 파이썬 패키지에 영향을 주게 된다.


이러한 문제를 해결하기 위한 최선의 방법은 무엇일까, 만약 프로젝트별로 독립된 실행환경을 갖고 있다면?


파이썬에서 이와 같은 역할을 해주는 기능이 바로 virtualenv 라는 패키지이다.



파이썬 IDE 를 통해 웹 자동화 프로젝트를 생성 후 터미널에 아래 명령어를 입력한다.


$ cd /Users/{사용자 이름}/{프로젝트 이름} $ python3 -m venv {가상환경 이름}


본인의 생성한 웹 자동화 프로젝트 디렉토리로 이동한 다음 가상환경을 생성하는 방법이다.



다음은 방금 생성한 가상환경을  적용시키는 방법이다. (Pycharm IDE 기준)


Preferences[command + ,] -> Project interpreter -> ⚙️(click) -> Add local -> Existing environment ->

...(click) -> /User/{사용자 이름}/{프로젝트 이름}/{가상환경 이름}/bin/python3 -> apply


잠시 파일을 종료 하였다가 다시 파일을 열어보자


프로젝트 디렉토리 경로 앞에 방금 적용한 가상환경 이름이 적혀있다면 성공이다.



이제 pip 명령어를 통해 가상환경에 selenium 패키지 파일을 설치해준다.


$ pip install selenium


디렉토리를 좀 정리해주자, 대충 이런 느낌이다.



__init__.py : 패키지로 인식하기 위한 파일, python 3.3 부터 작성하지 않아도 인식한다고 한다. 그래도 작성해주자 (파일명은 반드시 __init__)


test1.py : 테스트 코드를 작성할 파일이다. (이름은 아무렇게나 작성하면 된다.)


test_suite.py : 나중에 pytest 를 이용해 테스트 코드들을 한꺼번에 실행시켜주기 위해 미리 만들어 두었다.



이렇게 테스트 코드를 작성할 준비가 끝났다!





참고https://github.com/XXOK/XXOK-selenium


Selenium Webdriver


-  ID/Class/XPath/CSS 등을 이용해 Element를 지정하여 테스트 가능


-  다양한 브라우저(FireFox, Internet Explorer, Safari, Chrome...) 에서 일관성 있는 테스트 가능


-  multiple frames, multiple browser windows, popups and alerts, Drag-and-drop, AJAX-based UI elements 등 테스트 가능


참고 : http://docs.seleniumhq.org/projects/webdriver/