스프링노트(http://www.springnote.com)는 오픈마루 스튜디오(http://blog.openmaru.com)에서 개발한 서비스 형태의 위키(Wiki)다. 스프링노트는 그동안 ‘사발면 프로젝트’라는 프로젝트 이름으로 불려왔으며, 마소 3월호에 사발면 프로젝트의 기민한 방법론 도입 사례가 소개되면서 독자들에게 선 보인 적도 있다.
스프링노트는 글을 읽는 화면과 쓰는 화면 사이의 구분이 없기 때문에 글을 읽다가 수정하고 싶은 부분이 있으면 그 자리에서 바로 수정을 할 수 있는 ‘항상 편집’ 기능, 저장 버튼 없이 수정한 내용이 자동으로 저장되는 ‘자동 저장’ 기능, 위지윅(WYSIWYG: What You See Is What You Get) 편집 기능 등을 제공한다. 이를 위해 클라이언트 측 자바스크립트를 적극 활용하고 있고, XmlHttpRequest 객체를 이용한 비동기 통신도 빈번하게 이용한다.
또한 이 모든 기능이 현재 널리 쓰이는 다양한 브라우저에서 동일하게 수행되어야 하므로(현재 인터넷 익스플로러 6.0과 7.0, 파이어폭스 2.0을 지원하고 있고, 사파리 지원을 위한 작업이 진행 중이다) 브라우저 별로 모든 기능을 테스트하며 개발되어야 한다.
Ajax 애플리케이션에서 테스트 자동화의 중요성
웹 애플리케이션 개발 시 액티브X 같은 기술을 쓰지 않고 Ajax를 선택하게 되는 중요한 이유 중 하나는 애플리케이션이 여러 운영체제(맥, 리눅스, 윈도 등)와 브라우저(인터넷 익스플로러, 파이어폭스, 사파리, 오페라 등)에서 실행될 수 있도록 하는 데 있다. 즉 Ajax 애플리케이션은 태생적으로 다양한 플랫폼(브라우저)에서의 실행을 전제하고 있다.
다양한 플랫폼/브라우저를 고려한 Ajax 애플리케이션 개발에서 겪게 되는 가장 큰 문제는 브라우저별로 자바스크립트의 실행 방식, CSS를 해석하는 방법, DOM 지원 수준 등이 크게 다르다는 점이다. 그리고 동일한 브라우저라도 버전에 따라 차이가 있다는 점과 마지막으로는 같은 브라우저, 동일한 버전이라도 실행되는 운영체제가 다르면 작동 방식에 미묘한 차이가 있을 수 있다는 점이다. 따라서 다양한 플랫폼/브라우저에서 주기적으로 애플리케이션의 작동 상태를 테스트하는 것이 중요하다.
Ajax 애플리케이션 개발의 또 다른 문제는 자바스크립트 자체의 특성에서 기인한다. 자바스크립트는 동적인 언어이고 명시적인 컴파일 단계가 없기 때문에 코드를 수정하다가 문법 오류를 내더라도 해당 지점이 브라우저에서 실제로 실행되어 에러가 발생하기 전까지는 오류가 있다는 사실을 도무지 알 방법이 없다. 이 문제의 해결책 또한 최대한 많은 코드를 주기적으로 실행해보는 것이다.
때문에 주기적으로 다양한 테스트를 해 줘야 한다. 그렇다면 얼마나 자주 하는 게 좋을까? 자주하면 할수록 좋다. 버그를 빨리 발견 할수록 잡기가 수월하기 때문이다. 다시 말해, 한 달 전에 만들었던 코드를 디버깅하는 것 보다는 일주일 전에 만들었던 코드를 디버깅하는 것이 쉽고, 일주일 보다는 하루, 하루 보다는 몇 분 전에 짠 코드를 분석하는 것이 훨씬 쉽다.
결국 최대한 자주 최대한 다양한 기능을 최대한 다양한 플랫폼/브라우저 조합에서 반복적으로 테스트해야 한다는 결론에 이르게 되는데, 이게 바로 Ajax 애플리케이션에서 테스트를 자동화하는 것이 특히 중요한 이유다.
셀레늄을 이용한 기능 테스트
Ajax 애플리케이션의 테스트 자동화를 도와주는 도구는 여러 가지가 있지만 이 글에서는 셀레늄이라는 도구를 소개하려고 한다. 셀레늄(http://openqa.org/selenium/)은 실제 웹 브라우저 상에서 실행되는 테스트 자동화 툴이다. 테스트하고자 하는 웹 애플리케이션(AUT - Application Under Test)을 한 쪽 프레임(iframe)에 로딩한 후 다른 프레임에서 자바스크립트를 통해 자동화 테스트를 수행하는 방식으로 작동한다.
브라우저를 에뮬레이션 하거나 서버 측 로직만을 테스트하는 방식이 아니라 실제 브라우저 상에서 테스트를 진행하기 때문에 Ajax 애플리케이션처럼 클라이언트에 많은 로직이 있는 경우에도 이를 테스트하는 것이 가능해진다. 또한 테스트 시나리오를 한 번 작성해두면 여러 플랫폼/브라우저 조합에서 실행해볼 수 있기 때문에 여러분의 애플리케이션이 다양한 플랫폼/브라우저에서 제대로 수행되는지를 쉽게 확인할 수 있다.
테스터는 자동화를 위한 스크립트를 별도로 작성하지 않고 간단한 HTML 표(table)를 통해 테스트 시나리오를 작성할 수 있다(이 형식은 FIT -Framework for Integrated Testing- 와 유사하다. FIT에 대해서는 마소 2003년 6월호 테크니컬 칼럼에서 소개된 바 있다. 관심이 있는 독자들은 FIT 위키 -http://fit.c2. com- 도 살펴볼 것을 권한다).
.gif)
이 방식의 장점은 HTML 편집기만 있으면 누구나 쉽게(물론 약간의 학습이 필요하지만) 테스트 시나리오를 작성할 수 있다는 점과 누군가가 작성한 테스트 시나리오를 쉽게 읽고 이해할 수 있다는 점이다. 따라서 시나리오를 수정하기도 용이하다는 점 등이 있다. 잘 만들어진 셀레늄 테스트 시나리오 모음은 실행 가능한 요구사항 문서로도 유용하게 쓰일 수 있다.
셀레늄 IDE
수많은 테스트 시나리오를 하나씩 HTML 표로 만드는 작업은 상당히 지루하다. 이 작업을 쉽게 할 수 있도록 도와주는 도구가 있는데 바로 셀레늄 IDE이다. 셀레늄 IDE를 이용하면 브라우저에서 사용자가 한 모든 행위를 자동으로 셀레늄 테스트 시나리오 형태로 녹화하고 이를 약간 수정해서 적절한 단언(assertion)들을 넣어주는 방식으로 테스트 시나리오를 작성할 수 있다(소위 record-and-play 방식).
.gif)
셀레늄 IDE는 파이어폭스 브라우저의 확장 기능(extension) 형식으로 제공되기 때문에 반드시 파이어폭스가 설치되어 있어야 하지만, 일단 만들어진 테스트 시나리오 파일은 평범한 HTML이기 때문에 손으로 직접 만든 셀레늄 테스트 시나리오들과 동일하게 다양한 플랫폼/브라우저에서 실행할 수 있다. 셀레늄 IDE를 이용하여 구글의 웹 사이트를 테스트하는 예시를 보려면 http://barcamp.tistory.com/tag/Selenium에 접속해보길 바란다. 작년에 열린 바캠프 서울(BarCamp Seoul) 행사에서 발표한 것을 녹화한 15분 분량의 동영상 자료이다.
셀레늄과 Ajax 애플리케이션
셀레늄은 실제 브라우저를 사용하여 테스트를 수행한다는 점에서 Ajax 애플리케이션을 테스트하기에 적합한 면이 있지만, 또 다른 측면에서는 부족한 점도 있다. 셀레늄의 초기 설계가 전통적인 웹 애플리케이션을 가정하고 있기 때문이다. 다시 말해, 사용자가 뭔가를 입력하고 액션을 취하면(버튼이나 링크를 누르는 등) HTTP 요청이 보내지고 브라우저는 새로운 주소로 이동하는 방식을 가정하고 있으며, 수많은 “~~~AndWait” 명령들이 그 대표적인 예이다. 예를 들어 ClickAndWait 명령은 버튼 혹은 링크를 클릭하고 화면이 전환될 때 까지 기다리는 기능을 수행한다.
한편, Ajax 애플리케이션은 전통적인 웹 애플리케이션과 달리 화면은 전환하지 않은 채로 XMLHttpRequest 객체를 이용하여 서버와 비동기 통신을 한다. 따라서 셀레늄이 제공하는 ClickAndWait 명령을 통해서는 이러한 로직을 적절히 테스트할 수가 없다. 버튼을 클릭해도 화면 전환이 일어나지 않기 때문에 실행중인 테스트는 그 자리에서 한없이 멈춰서 기다리게 된다. 그렇다고 해서 ClickAndWait 대신 Click 명령(Click 명령은 ClickAndWait 명령과 다르게 특정 요소를 클릭하고 바로 다음 명령을 수행한다)을 쓰면 문제가 해결될까? 그렇지 않다. 이번에는 비동기 요청에 대한 응답이 미처 오기도 전에 다음 명령이 수행되는 바람에 테스트가 깨지는 문제가 발생할 것이다.
물론 셀레늄에서 기본으로 제공하고 있는 ‘pause’ 명령을 이용하면 비동기 통신을 시작하고 충분한 시간이 지날 때 까지 테스트를 일시정지(pause) 했다가 비동기 통신에 대한 응답이 온 이후에 assert 혹은 verify가 수행되도록 하는 꼼수를 쓸 수도 있다. 하지만 이것 역시 그리 좋은 방법이 아니다.
이보다 적절한 방법이 있다. 셀레늄은 테스터가 원하는 명령을 직접 추가할 수 있는 확장 메커니즘을 제공하고 있기 때문에, ClickAndWait류의 명령 말고 Ajax 애플리케이션의 비동기 통신 부분을 테스트할 수 있는 로직을 추가할 수 있다. 그리고 누군가가 이미 비동기 통신 부분을 테스트할 수 있는 확장 기능을 만들었으니 우리는 그걸 이용하기만 하면 된다(http://wiki.open qa.org/display/SEL/waitForCondition). 사용 방법은 다음과 같다.
.gif)
waitForCondition 명령은 두 개의 인자를 받는데, 첫 번째 인자는 주기적으로 수행(evaluation)될 자바스크립트 코드 블록이다. 이 코드 블록의 수행 결과가 true이면 다음 명령을 계속 진행하고 그렇지 않으면 계속 대기하며 true가 될 때까지 기다린다. 두 번째 인자는 밀리 초 단위의 최대 대기 시간 값이다. 즉 3초가 지나도록 계속 false이면 이 테스트가 실패한 것으로 간주한다.
waitForCondition 명령을 이용하면 비동기 호출을 보낸 후 그 결과로 화면의 상태나 특정 자바스크립트 객체의 상태가 변화될 때 까지 대기(wait for condition)하도록 하는 것이 가능해진다.
점진적으로 DSL 만들어가기
셀레늄은 다양한 명령들을 내장하고 있어서 거의 모든 상황에서 이들 명령만으로도 충분히 다양한 기능에 대한 테스트 케이스를 작성할 수 있다. 원하는 명령이 없다면 조금 전에 살펴본 바(waitForCondition)와 같이 user-extensions.js 파일을 수정하여 원하는 명령을 자유롭게 추가할 수 있다.
이제 셀레늄이 제공하는 명령들의 사용법을 하나씩 익혀가며, 또 명령이 없으면 하나씩 추가하는 식으로 테스트 시나리오를 쌓아가기만 하면 되는 걸까? 그렇지 않다. 테스트 시나리오가 ‘있다’, ‘없다’의 문제보다 더 중요한 문제가 남아있다. ‘얼마나 좋은 테스트 시나리오를 갖추었나?’ 하는 문제이다.
테스트 커버리지가 높아야 한다거나 하는 기본적인 사항들은 논외로 하고, 좋은 테스트 시나리오가 갖춰야 할 조건은 무엇일지 생각해 보자. 이는 좋은 코드가 갖춰야 할 조건과 크게 다르지 않다. 읽고 이해하기 쉬울 것, 쓰거나 고치기 쉬울 것.
읽고 쓰기 쉬운 테스트 시나리오를 만드는 전략 중 하나는 끊임없는 리팩토링을 통해 점진적으로 DSL(Domain-Specific Language)을 만들어가는 것이다. 예를 들어
“위에서 세 번째 입력창을 클릭하고 ‘H’를 입력하고, ‘e’를 입력하고, ‘l’을 입력 하고, ‘l’을 입력하고, ‘o’를 입력하고 입력창 옆에 있는 버튼을 누른다”
라는 시나리오 보다는
“[대화창]에 ‘Hello’를 입력하고 [전송] 한다”
라는 시나리오가 읽기에도 쉽고 쓰기에도 쉽다. 후자와 같은 시나리오가 가능하려면 [대화창], [전송]과 같은 해당 애플리케이션에 특정적인 개념들을 직접 명령으로 사용할 수 있어야 한다. ‘waitForCondition’을 추가하였듯이 ‘user-extensions.js’ 파일을 수정하면 이 같은 명령을 추가하는 것이 가능하다.
요는 단순히 원하는 명령이 있으면 그걸 쓰고, 없으면 추가한다는 개념이 아니라 테스트 시나리오에 같은 명령이 중복해서 나오면 이를 제거하기 위해 새로운 명령을 추가하고(중복 제거 리팩토링), 의도가 명확하지 않으면 의도를 더 잘 드러낼 수 있는 상위 개념의 명령을 추가하는(의도 드러내기) 식으로 user-extensions.js를 점진적으로 늘려나가는 것이 좋다는 얘기이다.
예를 들면, 스프링노트의 편집기 관련 부분을 테스트하는 시나리오 중 다음과 같은 것이 있다.
.gif)
‘[I]’는 캐럿의 위치를 뜻한다. 즉 문단(p)에 ‘* Hello’라고 입력하고 엔터를 누르면 자동으로 순서 없는 목록(ul)으로 바뀌고 다음 목록으로 캐럿이 이동해야 한다는 것을 나타내는 테스트 시나리오이다.
도메인 어휘들을 ‘점진적’으로 늘려가야 한다는 점도 중요한데, 초창기부터 모든 어휘를 예상하여 user-extensions.js를 무겁게 확장하는 것은 불필요한 낭비일 수 있다. 애플리케이션에 어떤 기능이 추가될 지, 테스트 시나리오에서 어떤 중복이 발생할 지 예상할 수 없기 때문이다. 지속적인 리팩토링을 통해 도메인 어휘는 자연스럽게 누적될 것이다.
제약에 얽매이지 않기
“웹 표준을 지키는 것이 좋다”, “액티브X를 쓰지 않는 것이 좋다” 등등 “~~하는 것이 좋다”는 얘기가 있는데, 특정 문맥을 고려하지 않고 항상 좋은 것이란 (거의) 없다. 셀레늄 테스트 시나리오는 일반 사용자들에게 보여 질 부분이 아니라 개발팀 내부에서만 실행된다는 점에서 일반 웹 애플리케이션과 다르다. 따라서 “웹 애플리케이션은 ~~하는 것이 좋다”라는 일반적인 원칙들을 그대로 따르지 않아도 되는 경우가 있다.
예를 들어 스프링노트의 편집기 부분을 테스트할 때 HTML 디자인모드(designMode - WYSIWYG 편집을 가능하게 해주는 브라우저 기능)에 특정 키 조합을 자동으로 밀어 넣어야 하는 경우가 있었다.
파이어폭스에서는 이벤트를 가상으로 발생시켰을 때 실제로 편집기에 해당 키가 눌린 것과 동일한 효과가 나타났었는데 인터넷 익스플로러에서는 보안 문제 때문이었는지 가상으로 이벤트를 발생시킨 것과 실제 키를 누른 것 사이에 차이가 나서 해당 기능을 자동화할 수 없었다.
고민 끝에 내린 결정은 액티브X를 사용하는 것이었다. 인터넷 익스플로러의 설정 창에서 셀레늄 테스트가 실행되는 웹 페이지에 대한 보안 수준을 낮춰서 임의의 액티브X가 실행될 수 있게 허용한 뒤에 Windows Scripting Host의 SendKeys를 이용하여 윈도우 메시지 루프에 직접 키 이벤트를 밀어 넣자 테스트가 제대로 수행되었다. 물론 파이어폭스에서는 자바스크립트만으로 충분히 액티브X를 대체할 수 있었으므로 브라우저별로 다른 코드가 수행되게 해두면 양 쪽 브라우저에서 해당 테스트를 수행할 수 있게 된다.
지금까지 셀레늄을 기준으로 테스트 자동화가 중요한 이유와 Ajax 애플리케이션을 테스트 하는 방법, 좋은 테스트를 만드는 방법 등을 살펴보았다.
웹 사이트에서 쉽게 찾을 수 있는 코드 예시나 명령어 설명 등은 최대한 생략했고, 독자가 직접 찾아볼 수 있도록 레퍼런스를 알려드렸다. 구체적인 방법 보다는 ‘왜 그렇게 해야 하는지’에 대해 설명하려고 노력했는데 일부 독자에게는 다소 추상적으로 느껴졌을지도 모르겠다. 본문 중에 인용한 참고자료를 아래에 모아두었으니 꼭 한 번씩 둘러볼 것을 권한다.
강규영 alankang@openmaru.com|이런 저런 잡다한 주제에 관심이 많아서 지적인 역마살이 낀 점을 제외하면 평범한 개발자. 오픈마루 스튜디오 웹서비스 개발팀에서 근무 중인 필자는 틈틈이 개인 위키(http://jania.pe.kr)를 운영하고 있다.
--------------
출처 : http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=30542 (새 창으로 열기)
rss