E2E 테스트란?
종단 간 (end to end) 테스트로 사용자가 직접 애플리케이션을 사용하는 것처럼 동작하도록 스크립트를 작성하고 이것을 실제 실행시켜보면서 기대한 대로 동작하는지 검증하는 테스트 방식이다.
예) 네이버 -> 로그인 -> 메일함 이동 -> 읽지 않은 메일 -> 특정 발신자 확인 -> 발신자가 보낸 메일이 있는지? Pass or Fail
대표적으로 Cypress, Testcafe, Nightwatch가 있고 각 장단점을 조사했다.
장점 | 단점 |
---|---|
모든 테스트코드가 내부엔진에 의해 비동기처리가 된다. | 여러 탭을 제공하지 않는다. 두개 이상의 브라우저를 테스트할 수 없다. |
커뮤니티가 크다 (공유된 많은 이슈를 통해 테스트코드 작성에 편리하다) | 단일 URL에서만 테스트진행이 가능하다. |
Cypress docs가 잘돼있다. 문서 안에 다양한 Bad/Good Case가 주석과 함께 잘 정리되어 있다. https://docs.cypress.io/guides/overview/why-cypress | |
자체적으로 제공되는 창에서 테스트가 진행되며 기록된다. (해당 스냅샷을 보면 코드로 작성한 브라우저의 액션을 확인할 수 있다) | |
테스트코드를 실시간으로 반영할 수 있어 디버깅, 테스트코드 작성이 쉽다. | |
Element 속성으로 지정된 값을(data-cy등) 별칭을 사용할 수 있어 테스트 깨짐의 위험이 적다. |
장점 | 단점 |
---|---|
다중 탭을 제공하며 url 전환이 가능하다. (특수한 케이스의 테스트 진행이 가능하다) | 기본 셀렉터만 제공한다. (APP 코드 수정 시 테스트가 깨지는 위험이 발생함) |
크로스 브라우저를 지원한다. | 커뮤니티가 작다. (이슈에 대한 정보가 부족하다) |
TestCafe Studio가 존재한다. (기존 웹사이트에서 사용자 액션을 취하면 액션에 해당하는 테스트코드로 변해 쉽게 테스트 코드를 작성할 수 있지만 유료이다) https://www.componentsource.com/ko/product/testcafe-studio/prices |
Selenium Webdriver란 브라우저 간에 테스트를 실행할 수 있는 웹 프레임워크인데 이전 Puppeteer 라이브러리를 사용하면서 Chrome 드라이버 호환되지 않아 버전에 맞게 Chrome 드라이버 버전을 낮추거나 webdriver 버전을 변경해야하는 문제가 있었다. 이처럼 브라우저에서 테스트하는 다른 프레임워크와 달리 dependency 설정을 해야한다.
NightWatch는 다른 프레임워크에 비해 장점이 없었고 커뮤니티가 작아 각종 테스트 케이스에 적합한지 조사과정에서 정보를 얻기 힘들어 제외했다.
진행할 테스트에 어떤 도구가 적합한지 알기 위해서 제공된 시나리오를 Cypress, Testcafe로 작성해 보았다.
공부하면서 봐온 문서 중 가장 정리가 잘 되어있다 생각했다!
https://docs.cypress.io/guides/overview/why-cypress
내가 원하는 요소를 잘 클릭하는지 위 사진처럼 쉽게 알 수 있다!!
안정성? 기존 E2E 테스트는 UI 렌더링 되는 시점이 제어가 잘 안 되어 불안정한 테스트 결과를 도출했다.
이를 해결하기 위해 Cypress는 큐 구조에 실행코드를 미리 적재한 후 내부엔진으로 비동기 처리하여 렌더링 된 이후 테스트가 진행되어 테스트 결과에서 안정성이 높다.
SSO 관련해 테스트 코드를 작성할 때 서비스를 클릭해 생성된 탭을 확인하기 어려웠다.
invoke라는 메서드로 a 태그에 있는 target 옵션을 제거해 현재 탭에서 새 URL이 열리게 한 후에 테스트 진행이 가능하지만. 테스트 후 돌아가는 과정이 불안정하여 테스트 코드로 작성하지 않았다.
하지만 SSO 특성상 여러 URL을 거쳐 최종 페이지에 도달하는 과정에서 안정된 테스트 결과를 도출하려면 많은 시간을 기다리게 해야 하는데 이는 테스트 코드에 적합하지 않다고 생각된다. (PC 사양마다 최종 페이지 도달까지의 걸리는 시간이 달랐다)
TestCafe 또한 설정에 Live 모드를 켜 코드 수정 시마다 테스트를 재 시작이 가능하지만, 가끔 CSS가 깨져 원활한 테스트 진행이 잘되지 않아 새로고침 하며 디버깅을 진행해 불편했다.
// APP Code <form id='test'> <button>제출</button> </form> ... // Test Code Cypress: cy.contains('button', '제출').click(); Testcafe: const button = t.Selector('button').withText('제출'); t.click(button); or t.click(Selector('button').withText('제출'));
- Cypress를 선택했다. 테스트 작성 시 안정성이 높았고, Mocha, chai 라이브러리 기반으로 이루어져 상대적으로 간편했다. (Mocha, chai를 이전에 경험했었다)
- 테스트 케이스를 억지로 성공시키는건 올바른 방법이 아니라고 판단하여 Testcafe의 장점인 멀티 탭 제공이 크게 와닿지 않았다.
- 테스트코드를 작성할 땐 data-cy를 활용하여 테스트 깨짐에 대처가 가능하다.
어떤식으로 테스트가 진행되는지?
이전에 테스트 코드를 한번 공부해볼까? 생각은 했었지만, 막상 테스트가 어떻게 동작해야 하는 거지? 무엇을 테스트해야 하지? 이 고민이 좀처럼 사라지지 않아 시작하기 어려웠다.
서비스에 대해 인증받는 과정이라 어떤 부분이 문제인지 대략적인 시나리오가 제공되서 첫 시나리오 작성에 대한 감을 잡기 시작했다.
내가 생각한 것보다 시간이 많이 들었다
기본적으로 Assertion 방법, Cypress에서 제공하는 많은 API와 기능 중 내가 테스트하는 서비스에 어떤 것이 적절한지 공부하고 적용하는 과정에서 많은 비용이 들었다.
개발환경….
WSL2를 설치해 개발하고 있었는데 많은 테스트 프레임워크는 손쉽게 사용할 수 있다며 홍보했지만, 나는 힘든 과정을 겪었다. 그 이유는 웹페이지를 테스트 할 땐 브라우저(크롬, 파이어폭스, 사파리)가 필요했지만,
WSL에선 브라우저, GUI가 제공되지 않아 기본적인 테스트를 띄우는데도 힘든 과정이었다...
해당 이슈는 X_Server를 활용하는 방법으로 해결했다.
https://stackoverflow.com/questions/62641553/setup-cypress-on-wsl-ubuntu-for-windows-10
https://nickymeuleman.netlify.app/blog/gui-on-wsl2-cypress
https://www.gregbrisebois.com/posts/chromedriver-in-wsl2/
visit : URL 방문, 기본적으로 config 파일에 설정된 baseUrl를 기준으로 이동한다. Cypress는 단일 사이트에서 테스트를 진행해야 하며 다른 사이트 방문이 기본적으로 불가능하다. ex) naver → google = 실패
다른 사이트로 이동해서 테스트하려면 우회적으로 테스트 코드를 작성해야한다.
get : Selector or alias(별칭)로 하나 이상의 요소를 가져올 수 있다.
contains : 원하는 텍스트를 포함한 요소를 가져올 수 있다.
type : 선택된 요소에 텍스트를 입력을 할 수 있다. ex)검색창에 값 넣기.
click : 선택된 요소에 클릭 이벤트를 발생시킨다.
wait : 일정시간동안 테스트를 멈춘다.
url : 현재 위치한 url 주소를 반환한다.
// APP https://example.com/hi 경로일때
<body>
<div id='title'> ==> 1번
<h2>제목</h2> ==> 1-1번
<p>부제목</p> ==> 1-2번
</div>
<div id='intro'> ==> 2번
<p>Cypress 소개</p> ==> 2-1번
</div>
<b>Cypress에 대한 설명란...</b> ==> 3번
<form> ==> 4번
<input className='query'/> ==> 4-1번
<button>제출</button> ==> 4-2번
</form>
</body>
// TEST CODE
{
cy.visit('/hi'); url/hi 경로로 이동
cy.get('#title > p'); 1번 제목 요소를 가져온다.
cy.contains('Cypress 소개'); 2-1번 요소를 가져온다.
cy.contains('b', 'Cypress'); 3번 요소를 가져온다.
cy.get('input').type('안녕하세요!!'); 4-1번에 `안녕하세요!!!` 를 입력한다.
cy.get('button').click() or cy.contains('button', '제출').click() 제출한다.
cy.url() => https://example.com/hi 반환
cy.wait(1000); // 1초 기다린 후 테스트가 종료된다.
}
위 예시처럼 기본적인 기능으로 간단한 테스트를 진행할 수 있었다.
테스트 코드를 작성함에 있어 같은 시나리오를 여러가지 방법으로 구현할 수 있어 작성에 크게 어려움은 없었다.
Cypress 공식문서 https://docs.cypress.io/
테스트 코드를 작성하며 개발자로서 도움이 되었던 부분은 테스트 코드 작성법뿐만 아니라 다양한 도구를 조사하고 사용해보며 정리하는 과정에서 개발자를 위한 도구를 내가 가진 환경에 어떤 도구의 선택이 적합한지 판단하는 시간을 가지는게 생각보다 더욱더 중요한 걸 깨달았다.