Selenium으로 Ansible 웹 앱 Role 테스트하기
개요
Ansible은 웹 앱을 배포하는 데 탁월한 도구입니다. Ansible을 사용하면 웹 앱을 구성하는 다양한 “role”(예: 웹 서버, 데이터베이스 서버, 애플리케이션 서버) 단위로 웹 앱을 정의할 수 있습니다. Role이 복잡해지고 Role 간의 상호작용이 늘어날수록, 웹 앱이 올바르게 배포되었는지 검증하기 위해 그에 걸맞게 더욱 강력한 테스트 방법이 필요합니다.
지난 포스트에서는 ClipBucket이라는 동영상 호스팅 웹 앱을 Ansible로 배포했습니다. 당시 포스트에서는 웹 앱이 올바르게 설치되었는지 확인하는 자동화된 테스트를 포함했지만, 애플리케이션 기능을 그리 엄격하게 검증하지는 못했습니다.
이번 포스트에서는 앱의 기능을 더 깊이 있게 검증하는 한층 강력한 자동화 테스트를 보여드리겠습니다. 이를 위해 Selenium이라는 웹 브라우저 자동화 도구를 사용합니다. 여기서는 ClipBucket role을 계속 사용하지만, 여기서 소개하는 아이디어는 Ansible로 배포하는 다른 웹 앱에도 일반적으로 적용할 수 있습니다.
curl을 이용한 기본 테스트
원래 빌드 스크립트에서 마지막 테스트는 다음과 같았습니다:
curl -s "${container_ip}" \
| grep "Login" && \
(echo 'Landing page test: pass' && exit 0) || \
(echo 'Landing page test: fail' && exit 1)이 테스트는 웹 앱의 몇 가지 중요한 속성을 검증하므로 유용합니다. 구체적으로는 다음과 같습니다:
- 웹 서버가 80번 포트에서 대기 중인지
- 웹 서버가 사용자 요청에 응답하는지
- 웹 서버가 ClipBucket 랜딩 페이지를 정상적으로 제공하는지
더 나은 테스트가 필요한 이유
기존 테스트가 중요한 검사를 제공하기는 했지만, 웹 앱의 실제 기능은 거의 검증하지 못했습니다. Ansible role에 버그가 들어가 웹 앱이 고장 나더라도 기본 테스트에서는 이를 감지하지 못할 수 있습니다.
예를 들어 ClipBucket 플레이북에서 중요한 태스크를 실수로 삭제했다고 가정해 보겠습니다:
diff --git a/tasks/main.yml b/tasks/main.yml
index 8542ffc..e9d42c0 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -29,15 +29,6 @@
name: flvtool2
state: present
-- name: create a symlink for ClipBucket to find ffmpeg and MP4Box
- file:
- path: "/usr/local/bin/{{ item }}"
- src: "/usr/bin/{{ item }}"
- state: link
- with_items:
- - ffmpeg
- - MP4Box
-
- name: install the Python MySQLB module
pip: name=MySQL-python이렇게 수정된 플레이북으로 배포한 뒤 대상 서버에 접속해 보면, 모든 것이 정상으로 보입니다:
모든 설치 태스크가 성공하고 ClipBucket 랜딩 페이지도 정상적으로 표시됩니다. 무엇이 문제일까요?
동영상을 업로드해 보겠습니다. 영상을 재생하려고 하기 전까지는 모든 것이 정상적으로 동작합니다:
플레이북에서 ffmpeg에 대한 심볼릭 링크를 생성하는 태스크를 삭제했기 때문에, ClipBucket이 동영상을 스트리밍 가능한 형식으로 트랜스코딩하지 못합니다.
이 문제를 검증하는 자동화 테스트를 만들고 싶지만, 간단한 커맨드라인 도구로 동영상 업로드를 스크립트화하기는 어렵습니다. 사용자는 먼저 로그인해야 하고(즉, 스크립트가 요청 간 쿠키를 관리해야 합니다), 그 다음 웹 UI를 탐색해 동영상을 업로드해야 합니다. 이를 일련의 curl 명령으로 구현하는 것은 매우 어렵습니다.
다행히 Selenium을 사용할 수 있습니다. Selenium은 웹 브라우저 동작을 프로그래밍 방식으로 수행할 수 있게 해주는 웹 테스트 도구입니다.
Selenium 설정하기
Selenium을 시작하려면 Ansible 컨트롤 머신에 몇 가지 구성 요소를 설치해야 합니다:
- Selenium Python 패키지 - Python API를 사용할 예정이며, 이 패키지를 통해 Selenium 프레임워크와 Python 바인딩을 얻을 수 있습니다.
- Firefox - Selenium이 제어할 브라우저가 필요합니다. Selenium은 대부분의 주요 브라우저에서 동작하지만, Firefox를 네이티브로 지원합니다.
- xvfb - 실제 디스플레이가 없는 VM에서 테스트를 실행할 예정이므로, Firefox가 모니터에서 실행되는 것처럼 인식하도록 가상 디스플레이인 xvfb를 사용합니다.
이를 위해 비교적 간단한 플레이북을 만들 수 있습니다. 조금 까다로웠던 부분은 xvfb가 직관적이지 않은 init 스크립트를 필요로 한다는 점이었습니다. 다행히 블로거 Cory Klein이 작년에 xvfb init 스크립트 예제를 담은 포스트를 작성했고, 그의 예제를 활용해 필요에 맞는 스크립트를 만들 수 있었습니다.
테스트할 웹 플로우 선택하기
이제 Selenium을 설치했으니 웹 앱을 검증할 Selenium 스크립트를 만들 차례입니다. 검증하고 싶은 웹 플로우는 다음과 같이 다양합니다:
- 로그인
- 동영상 업로드 및 재생
- 동영상에 댓글을 작성하고 정상적으로 표시되는지 확인
- 새 사용자 계정 생성
ClipBucket의 경우 동영상이 올바르게 업로드되는지 확인하는 것이 특히 중요하므로, 로그인과 동영상 업로드를 테스트해야 합니다. 안타깝게도 ClipBucket은 업로드를 관리하기 위해 무거운 JavaScript 패키지를 사용하므로, 파일을 업로드하는 일반적인 Selenium API가 동작하지 않습니다.
대안으로 ClipBucket 모듈 진단 페이지를 활용하겠습니다. 이 페이지는 ClipBucket에 설치된 모듈을 보여주고, 모듈이 제대로 설치되지 않았을 때 오류 메시지를 표시합니다:
동영상 업로드 플로우 대신 이 페이지를 활용해 모든 모듈이 올바르게 설치되었는지 검증할 수 있습니다.
이제 어떤 기능을 검증할지 정했으니, 자동화할 웹 플로우를 개략적으로 정리해 보겠습니다. 대략 다음과 같습니다:
- ClipBucket URL 로드
- 관리자 계정으로 로그인
- 모듈 진단 페이지로 이동
- 모든 모듈이 설치되었는지 확인
이를 통해 기본 테스트에서는 검증하지 못했던 몇 가지 추가 기능을 자동으로 검증할 수 있습니다:
- 사용자 로그인이 동작함(ClipBucket이 데이터베이스에 정상적으로 접근할 수 있다는 의미입니다)
- ClipBucket이 필요한 도구 의존성에 접근할 수 있음
웹 플로우 자동화하기
Selenium에서 브라우저 동작을 자동화하려면 먼저 브라우저에서 로드할 URL을 Selenium에 알려주고, 페이지에서 요소를 찾고 상호작용하는 방법을 지정해야 합니다. 원하는 웹 플로우에 필요한 동작을 어떻게 자동화하는지 살펴보겠습니다.
로그인 자동화
로그인하려면 로그인 페이지에서 자격 증명 필드를 찾고, 사용자 이름과 비밀번호를 입력한 뒤 “Login” 버튼을 눌러야 합니다. 다행히 사용자 이름과 비밀번호 필드에는 id 속성이 있어 페이지에서 쉽게 식별할 수 있습니다. “Login” 버튼에는 id 속성이 없지만 login이라는 name 속성이 있고 페이지 내에서 고유하므로 이를 식별자로 사용할 수 있습니다:
다음 코드 스니펫에서 이러한 필드를 찾고 로그인 자격 증명을 입력합니다:
self.get('/admin_area/login.php')
username_element = self._driver.find_element_by_id('username')
ui.WebDriverWait(
self._driver, TIMEOUT).until(
expected_conditions.visibility_of(username_element))
password_element = self._driver.find_element_by_id('password')
username_element.send_keys(username)
password_element.send_keys(password)
self._driver.find_element_by_name('login').click()모듈 목록 스크래핑
설치된 모듈 페이지를 확인하는 작업은 조금 다릅니다. 페이지와 상호작용할 필요는 없고, 단지 페이지 요소를 찾아 해석하기만 하면 됩니다. 모든 모듈이 올바르게 설치되었는지 확인해야 하므로, 각 모듈을 나타내는 박스를 식별한 뒤 오류 메시지를 표시하는지 프로그래밍 방식으로 판단해야 합니다.
관심 있는 요소들(또는 DOM 상의 부모 요소들) 중 어느 것에도 id 속성이 없어 다소 까다롭습니다. 모두 class="well"인 <div>이므로, 각 모듈 정보 박스를 찾는 데는 이것이 최선의 방법입니다.
박스를 찾은 뒤에는 해당 박스가 모듈 설치 성공을 나타내는지, 문제를 나타내는지 판단해야 합니다. 성공 지표를 찾을 수도 있고, 실패 지표가 없음을 확인할 수도 있습니다. 전자가 조금 더 엄격하지만, 후자가 코드로 구현하기는 더 간단합니다. 오류 메시지가 있는 박스에는 항상 class="alert" 속성을 가진 요소가 포함되므로, 이 클래스를 가진 자식 요소가 없으면 성공한 박스로 판단할 수 있습니다.
다음 코드 스니펫으로 이를 구현할 수 있습니다.
self.get('/admin_area/cb_mod_check.php')
for module_element in self._driver.find_elements_by_class_name('well'):
try:
ui.WebDriverWait(
self._driver, TIMEOUT).until(
expected_conditions.visibility_of(module_element))
alert_element = module_element.find_element_by_class_name(
'alert')
if alert_element:
raise ClipBucketModuleError(alert_element.text)
except exceptions.NoSuchElementException:
# Lack of alert is good: the module is installed correctly.
continueTravis에 웹 플로우 테스트 통합하기
이제 모든 것을 종합해 Selenium 설치 플레이북과 Selenium 스크립트를 빌드 파일에 추가할 수 있습니다.
이제 앞서 만든 고장 난 커밋으로 GitHub 풀 리퀘스트를 생성합니다. Travis 빌드가 실행되면 실패하는 것을 확인할 수 있습니다:
2016-09-21 00:17:06,229 clipbucket_driver INFO Logging in with username=testadmin
2016-09-21 00:17:06,229 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 00:17:07,525 clipbucket_driver INFO Login complete
2016-09-21 00:17:07,526 clipbucket_driver INFO Checking ClipBucket modules
2016-09-21 00:17:07,526 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
Traceback (most recent call last):
File "tests/clipbucket_driver/main.py", line 36, in <module>
main(parser.parse_args())
File "tests/clipbucket_driver/main.py", line 24, in main
driver.do_check_modules()
File "/home/travis/build/mtlynch/ansible-role-clipbucket/tests/clipbucket_driver/clipbucket_driver.py", line 64, in do_check_modules
raise ClipBucketModuleError(alert_element.text)
clipbucket_driver.ClipBucketModuleError: ffmpeg is not found훌륭합니다! 새로운 테스트가 Ansible role의 문제를 정확히 감지했습니다.
그리고 정상적인 플레이북을 실행했을 때 모든 것이 잘 동작하는지도 확인해 보겠습니다:
2016-09-21 01:58:21,992 clipbucket_driver INFO Logging in with username=testadmin
2016-09-21 01:58:21,992 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 01:58:23,036 clipbucket_driver INFO Login complete
2016-09-21 01:58:23,039 clipbucket_driver INFO Checking ClipBucket modules
2016-09-21 01:58:23,040 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
2016-09-21 01:58:23,724 clipbucket_driver INFO Module check complete
2016-09-21 01:58:23,724 clipbucket_driver INFO Exiting ClipBucket driverSelenium 스크립트가 모든 필수 모듈이 올바르게 설치되었음을 정확히 판단했습니다. 향후 로그인 기능이 실패하거나 모듈이 잘못 설치되도록 하는 변경이 발생하더라도 자동화된 테스트가 이를 잡아낼 것입니다.
결론
이번 포스트에서는 Ansible과 Selenium을 결합해 Ansible role이 웹 앱을 올바르게 배포했는지, 그리고 그 결과 배포가 예상된 사용자 동작을 지원하는지 검증했습니다. Selenium은 매우 유연하므로 다양한 웹 애플리케이션에서 여러 가지 웹 UI 플로우를 테스트할 수 있습니다.
글을 무작위로 읽기




