Vue.js 공식 홈페이지에서는 단위 테스팅(Unit Testing)을 위한 테스트 러너로써 Karma를 적극 권장하고 있다.

 

어떤건지 궁금해서 한번 써본 적은 있지만, 실무에 적용해보기에 앞서 너란 놈에 대해 깊은 공부가 필요함을 느끼고...

 

정말 오랜만의 포스팅 주제로 삼았다.

 

 

KARMA

공식 홈페이지에 가보면, 이렇게 소개하고 있다.

 

A tool which spawns a web server that executes source code against test code for each of the browsers connected.

 

위의 영어가 너무 어려워서, 아래처럼 아름다운 한글로 풀어봤다.

 

카르마는 어떤 웹 서버를 생성하는 도구이다.

이 웹 서버는, 다양한 브라우저들과 연결되어 있다.

그리고, 이 웹서버는 연결되어 있는 각 브라우저 위에서 테스트를 수행한다.

 

그렇다. 이 친구의 목적은 하나다.

개발자들이 다양한 브라우저 환경에서 단위 테스트를 손쉽게 할 수 있는 아름다운 테스팅 환경을 제공하는 것.

 

그렇다면, 브라우저가 연결되어있어야 테스트가 가능한데,

카르마가 생성한 테스팅을 수행하는 웹서버는 어떻게 브라우저를 감지할까?

 

 

브라우저 감지

아래 두 가지의 방법으로 브라우저를 감지한다.

  1. 개발자가 손수 브라우저를 열어준다.
  2. 자동으로 감지하도록 할 브라우저를 카르마 config 파일에 지정해준다.

1. Manually

첫 번째 방법, 즉 손수 진행하려면 

테스트를 수행하려는 브라우저를 열고, 카르마 웹 서버가 돌고 있는 URL을 찍어준다.

 

http://<hostname>:<port>/ 

이렇게 주소를 찍고 들어올텐데, 여기서 <hostname>은  카르마 서버가 돌고 있는 그것일테고,

<port>는 그 서버가 listening하고 있는 그것임.

 

근데 로컬에서 띄우고 별 일 없으면 그 주소는 그냥 http://localhost:9876/ 일 것이다.

 

이렇게 손수 브라우저를 띄우는 방법은, pc가 아니라 모바일 환경에서 테스팅할 때 유용하다.

(물론 카르마 서버랑 같은 네트워크여야 하겠지)

 

2. Auto

그러나 웹 개발자라면, 손쉽게 자동으로 감지하도록 카르마에게 시키는 것이 아름다워 보인다.

그러려면 configuration 파일에 자동으로 띄울 브라우저 리스트를 지정해주어야 하는데, 아래처럼 하면 된다.

browsers: ['Chrome']

이렇게 하면 카르마는 알아서 자동으로 브라우저를 감지하고, 테스트를 모두 수행하면 자동으로 죽인다.

(configuration 파일에 대한 자세한 내용은, 이 포스트에서는 다루지 않기로 한다.)

 

 

이 때, 카르마는 브라우저 런처(browser launcher)를 이용해서 브라우저를 띄우는데,

사용 가능한 브라우저 런처 리스트는 아래와 같다.

대부분의 이 브라우저 런처들은 플러그인 형태로 끼워지는 것이다.

그러므로, 우선적으로 런처가 설치되어야 한다. 아래 예시처럼.

# Install the launcher first with NPM:
$ npm install karma-firefox-launcher --save-dev

 

이거슨 누군가가 미리 만들어놓은 브라우저 런처 플러그인이고, 우리가 손수 커스텀 플러그인을 만들어도 된다. 응 그냥 갖다 쓸래

 

 

그럼 이제 자동으로 띄운 브라우저에서, 어떻게 테스트 명령을 내리고 또 테스트를 실행한 결과를 받아오는지 알아보자.

 

 

웹 소켓을 통한 연결

카르마는 웹 소켓을 이용해 브라우저가 카르마 웹 서버와 커넥션을 가지도록 한다.

 

카르마는 config 에 정의된 브라우저들을 띄운 후, 시작 페이지를 카르마 서버의 URL 로 설정한다.

그리고 이 페이지가 브라우저에서 실행될 때, 웹 소켓을 통해 서버에 연결되는 것이다.

 

카르마 서버는 웹 소켓 연결을 확인하면, 클라이언트 페이지에게 테스트 실행을 지시한다. 

 

웹 소켓으로 연결되어 있기 때문에 언제든지 테스트 수행 명령을 내릴 수 있는데,

이는 카르마가 테스팅 환경으로써 넘나 훌륭한 이유라고 할 수 있다.

클라이언트 페이지의 요청이 없어도, 언제든지 카르마 서버의 필요에 의해 테스트 수행 명령을 내릴 수 있기 때문이다.

덕분에 파일의 변화가 일어날 때마다 자동으로 테스트가 실행될 수 있다.

 

 

파일 변화 자동 감지

카르마는 FS Model이라는 놈을 가지고 있다. 

소스코드 파일의 가장 마지막 버전의 타임스탬프라고 보면 된다.

 

그리고 또, FS Watcher 라는 놈도 있다.

이거슨 테스트 중인 프로젝트의 파일들에 변화가 일어나는지 감시하고, 이를 FS Model에 반영하는 역할을 한다.

 

시나리오는 대충 이렇다.

 

1. 소스코드에 변화가 일어난다.

2. FS Watcher는 이를 감지하여, FS Model의 API를 이용해 FS Model을 업데이트한다.

3. FS Model은 변경 사항이 생기면 이벤트를 발생시킨다.

4. 카르마 웹 서버는 FS Model의 변경 이벤트를 Listen하고 있다가, 변경 이벤트가 발생하면 context.html을 재생성한다.

5. 그리고 config에 restartOnFileChange가 true인 경우, 웹 소켓 커넥션을 통해 클라이언트 페이지에게 테스트 재시작을 지시한다.

 

이는 매번 소스코드에 변경이 일어날때마다 reload를 위해 해야 했던 많은 번거로운 작업들을 줄여준다.

진정한 의미의 테스트 자동화라 할 수 있겠다.

 

 

IFrame 기반 테스트

설명을 위해 위의 시나리오를 이어나가 보겠다.

 

6. 서버로부터 테스트 실행 지시를 받은 클라이언트 페이지는 iframe을 연다.

7. 그리고 서버에 context.html 을 요청해서 받아와, iframe 안에 페이지를 생성한다.

 

context.html테스트 프레임워크 어댑터소스코드(테스트의 대상), 그리고 테스트코드를 포함한다.

즉, 테스트에 필요한 모든 요소가 iframe 안에 들어간다고 보면 된다.

(테스트 프레임워크 어댑터는 밑에서 다시 설명하겠음.)

 

테스트 실행 명령이 떨어질 때마다, 클라이언트 페이지는 이 iframe을 다시 로드한다.

즉, 서버는 매번 바뀐 내용을 감지하여 context.html을 생성할테고, 바뀐 내용을 가지고 테스트를 실행하게 되는 것이다.

 

8. context.html 페이지는 로드가 끝나면, onload 이벤트를 발생시킨다.

9. 컨텍스트 페이지(context.html)의 onload 이벤트를 감지한 클라이언트 페이지는, postMessage를 통해 컨텍스트 페이지(context.html)를 클라이언트 페이지에 연결(Listen 이벤트 등록)한다.

 

 

<참고1 - postMessage>

window.postMessage() 메소드는 Windows 오브젝트 사이에서 안전하게 cross-origin 통신을 할 수 있게 한다.

예로, 페이지와 생성된 팝업 간의 통신이나, 페이지와 페이지 안의 iframe 간의 통신에 사용할 수 있다.


이렇게 연결이 되고나면, 이제 남은 일은 테스트를 실행하는 것. 그리고 그 결과를 잘 보여주는 것.

 

글탐 그 일은 누가 하느냐?

컨텍스트 페이지(context.html)에 포함된 테스트 프레임워크 어댑터를 기반으로 테스트 코드가 실행되고,

테스트 프레임워크 리포터가 결과를 전달받아 예쁘게 잘 보여준다.

 

밑으로 고고.

 

 


<참고2 - 테스트 프레임워크 & 테스트 러너>

자알~ 가다가, 갑자기 '테스트 프레임워크' 하니까 갑자기 헷갈리고, 급 의욕 떨어질 것 같아서 설명한다.

아니, 카르마는 테스트 프레임워크가 아닌건가??
흠. 위에 설명을 보니, 카르마는 테스트 러너라네.
읭? 테스트 러너가 테스트 프레임워크 아녔어? 먼솔?

카르마는 테스트 러너로, 자동화된 테스트를 보다 간단하고 빠르게 만들어주는 테스트 환경 툴이다.
그리고 앞으로 언급할 테스트 프레임워크는 Mocha, Jasmine과 같이 자동화된 테스트를 지원해주는 도구이다.
테스트 러너 위에서 테스트 프레임워크를 이용한 테스트가 이뤄진다고 볼 수 있겠다.

읭? 그런데 우리는 이전에 카르마 없이도 Mocha와 같은 테스트 프레임워크로 테스트 잘만 하였는데?

그럴 것이다. 왜냐면 우리가 잘 아는 테스트 프레임워크들도 기본적으로 테스트 러너를 제공하기 때문이다.

그러나.
그 제공한다는 러너가 빈약하기 짝이 없다.

예를 들어 Mocha의 경우, Node 기반의 러너를 제공한다.
Node 테스트만 진행한다면 문제 없겠지만 브라우저 테스트의 경우에는 얘기가 다르다.
DOM 과 같은 브라우저 API는 없기 때문에 cross-browser 문제를 이 방식으로 테스트 하는 데에는 한계가 있다.

물론, 대부분의 테스트 프레임워크에서 사용하는 HTML 러너와 같은 방식으로 브라우저에서도 실행할 수 있지만, 개발자는 테스트를 실행하려면 매번 브라우저를 열고, 러너를 다시 로드해야 하는 불편함을 감수해야 한다.
우리는 이런거 안하려고 카르마 쓰는 거고.

 

테스트 프레임워크 어댑터

Mocha, Jasmine과 같은 기존의 테스트 프레임워크를 사용하려면,

해당 프레임워크를 카르마에서 사용하기 위한 어댑터가 필요하다.

 

각 테스트 프레임워크에는 테스트를 실행하고 결과를 보고하기 위한 서로 다른 API가 있다.

어댑터는 기본적으로 테스트 프레임워크와 카르마 클라이언트 매니저 API 간의 통신을 변환하는 역할을 한다.

(클라이언트 매니저 설명은 밑에 있음)

 

카르마 config 파일에 원하는 테스트 프레임워크 어댑터를 꽂아주면 된다.

물론, 당연히 해당 플러그인이 설치되어 있어야 할 것이다.

// karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '../..',
    frameworks: ['jasmine'],
    //...
  });
};

 

 

클라이언트 매니저

클라이언트 매니저 카르마 서버와 테스트 프레임워크 어댑터가 통신하기 위한 API를 제공한다.

 

읭? 

혼돈의 카오스를 막기 위해 그림을 제공하겠다.

클라이언트 구조

카르마 서버(A)   <----> 클라이언트 매니저(B) <--(iFrame)--> 테스트 프레임워크 어댑터(C) <----> 테스트 프레임워크(D)

 

매니저(B)는 서버(A)가 프레임워크(D)와 통신하기 위한 통로이고,

어댑터(C)는 매니저(B)와 프레임워크(D)가 서로 알아들을 수 있도록 통신 내용을 변환하는 통역자 역할이라고 이해하면 된다.

 

이렇게 매니저를 통해서 클라이언트 페이지로부터 테스트 수행 지시가 떨어지면,

10. 컨텍스트 페이지의 프레임워크 어댑터는 테스트를 수행 지시를 프레임워크에 전달한다.

11. 테스트 프레임워크는 테스트를 수행한다.

12. 성공/실패 여부는 이벤트를 발생시킴으로써 클라이언트 페이지로 postMessage를 통해 전달된다.

13. 클라이언트 페이지는 다시 웹 소켓을 통해 카르마 서버에 결과를 전달한다.

 

 

테스트 프레임워크 리포터

14. 서버는 클라이언트로부터 메시지를 받으면, '브라우저' 이벤트를 발생시킨다.

15. 리포터는 서버의 '브라우저' 이벤트를 감지하여 결과 데이터를 얻는다.

 

리포터는 데이터를 인쇄하거나 파일로 저장하거나, 다른 서비스로 데이터를 전달할 수도 있다.

 

여기서 중요한 점은, 어댑터와 리포터는 거의 항상 쌍으로 제공된다는 점이다.

 

카르마는 사용자가 어떤 테스트 프레임워크를 사용할지 알 수 없고, 알 필요도 없다.

해당 프레임워크의 데이터 포맷 역시 알지 못한다.

 

결과 데이터는 테스트 프레임워크에 의해 생성되는 것이고,

어댑터에 의해 전달되는 것이고,

리포터에 의해 표현되는 것이다.

 

어떤 프레임워크를 사용하느냐에 따라 어댑터와 리포터는 거의 쌍으로 결정된다고 생각하면 된다.

 

 

 

마치며...

그냥 카르마는 대충 이런거다 설명하려다보니 생각보다 깊게 들어간 것 같다.

이렇게까지 깊게 알 필요는 없었는데;

 

웹 어플리케이션 단위 테스트를 알아보다 보니, 너무나도 많은 테스팅 툴이 있는데

어떤 툴은 다른 툴을 내부적으로 사용하고 있고,

어떤 애들은 비교당하고 있고...

 

도대체 뭐가 뭔지 뒤죽박죽이여서 카르마부터 파보자, 하고 알아봤더니

전체적인 그림이 그려지는 것 같다.

 

굿굿.

하.. 그래서 난 무슨 프레임워크 쓰지.

 

 

 

 

- end 

 

+ Recent posts