나의 첫 테스트코드

ukwon·2022년 9월 5일
2
post-thumbnail

첫 테스트코드 작성까지

✏️ 2년 동안 프로그래밍 공부를 해오면서 처음으로 테스트 코드를 작성했다. 그동안엔 코드를 작성하고 실행하여 원하는 기능이 제공되는지 확인한 후, 다음 기능을 개발했는데 작은 프로젝트나 혼자 개발하는 환경에선 구현한 기능이 사이드 이펙트가 발생하는 경우가 적어 테스트코드 관련해 필요성을 크게 느끼지 못했고, 그 때문에 관련 공부를 하지 않았었다. 그렇게 공부를 해오면서 프론트 개발자로 회사에 입사하게 되었고 혼자서 공부하고 원하는 기능을 개발하던 방식에서 기획에 맞춰 원하는 기능을 제공하고, 백엔드 개발자와 협업하는 과정에서 테스트 코드에 관심이 생기게 되었다.

관심이 생긴 이유?

  1. 협업하게 되면서 API가 변경되어 해당 API가 여러 곳에서 사용하는 경우 테스트 못한 부분을 지나쳐 개발하다 전체적인 테스트를 다시 진행할 때 이슈를 발견하게 되면 단번에 알아내기 힘든 이슈로 개발자에게 돌아와 원인을 분석하게 된다.
    우스갯소리로 어제 작성한 코드도 왜 이렇게 작성했지? 하며 개발하는 과정에서 이슈의 원인을 찾으려면 비용이 상당했다.
  2. 리팩토링을 진행하기 어려웠다. 데드라인이 존재했고, 생산성을 위해 코드 가독성을 떨어트리거나 급하게 구현한 기능, 재사용을 하지 않는 부분들이 프로젝트를 진행하면서 생겨났는데
    리팩토링 과정에서 계속 웹사이트와 IDE를 번갈아 가며 제대로 동작하는지 확인하며 리팩토링을 진행하니 효율적이지 못했고 모든 경우를 항상 같은 조건으로 반복하여 테스트하기 어려웠다.

첫 테스트 방법으로 E2E 유형을 선택했다

E2E 테스트란?
종단 간 (end to end) 테스트로 사용자가 직접 애플리케이션을 사용하는 것처럼 동작하도록 스크립트를 작성하고 이것을 실제 실행시켜보면서 기대한 대로 동작하는지 검증하는 테스트 방식이다.

예) 네이버 -> 로그인 -> 메일함 이동 -> 읽지 않은 메일 -> 특정 발신자 확인 -> 발신자가 보낸 메일이 있는지? Pass or Fail

  • 사용자 시점으로 테스트한 시나리오가 제공되었고 사용자 시점으로 테스트하는 E2E 테스트가 적절했다.

E2E Testing 도구 비교

대표적으로 Cypress, Testcafe, Nightwatch가 있고 각 장단점을 조사했다.

1. Cypress

장점단점
모든 테스트코드가 내부엔진에 의해 비동기처리가 된다.여러 탭을 제공하지 않는다. 두개 이상의 브라우저를 테스트할 수 없다.
커뮤니티가 크다 (공유된 많은 이슈를 통해 테스트코드 작성에 편리하다)단일 URL에서만 테스트진행이 가능하다.
Cypress docs가 잘돼있다. 문서 안에 다양한 Bad/Good Case가 주석과 함께 잘 정리되어 있다. https://docs.cypress.io/guides/overview/why-cypress
자체적으로 제공되는 창에서 테스트가 진행되며 기록된다. (해당 스냅샷을 보면 코드로 작성한 브라우저의 액션을 확인할 수 있다)
테스트코드를 실시간으로 반영할 수 있어 디버깅, 테스트코드 작성이 쉽다.
Element 속성으로 지정된 값을(data-cy등) 별칭을 사용할 수 있어 테스트 깨짐의 위험이 적다.

2. TestCafe

장점단점
다중 탭을 제공하며 url 전환이 가능하다. (특수한 케이스의 테스트 진행이 가능하다)기본 셀렉터만 제공한다. (APP 코드 수정 시 테스트가 깨지는 위험이 발생함)
크로스 브라우저를 지원한다.커뮤니티가 작다. (이슈에 대한 정보가 부족하다)
TestCafe Studio가 존재한다. (기존 웹사이트에서 사용자 액션을 취하면 액션에 해당하는 테스트코드로 변해 쉽게 테스트 코드를 작성할 수 있지만 유료이다) https://www.componentsource.com/ko/product/testcafe-studio/prices

3. NightWatch

  • 장점은 Testcafe와 유사하지만, 단점으로 Selenium Webdriver 기반으로 돌아가 두 프레임워크에 비해 종속성이 크다.
    (NightWatch가 아닌 Selenium Webdriver 이슈와 마주하게 된다)
  • 유일한 장점은 X_Path를 기본적으로 제공하지만 위 프레임워크 모두 추가라이브러리로 해당 기능을 대체할 수 있다.

    Selenium Webdriver란 브라우저 간에 테스트를 실행할 수 있는 웹 프레임워크인데 이전 Puppeteer 라이브러리를 사용하면서 Chrome 드라이버 호환되지 않아 버전에 맞게 Chrome 드라이버 버전을 낮추거나 webdriver 버전을 변경해야하는 문제가 있었다. 이처럼 브라우저에서 테스트하는 다른 프레임워크와 달리 dependency 설정을 해야한다.

Cypress VS Testcafe

NightWatch는 다른 프레임워크에 비해 장점이 없었고 커뮤니티가 작아 각종 테스트 케이스에 적합한지 조사과정에서 정보를 얻기 힘들어 제외했다.

진행할 테스트에 어떤 도구가 적합한지 알기 위해서 제공된 시나리오를 Cypress, Testcafe로 작성해 보았다.

Cypress

  1. Cypress는 docs가 잘 작성되어 있었다. 간단한 설명과 API 사용법을 다양하게 조합하여 제시해주어 간단한 테스트는 공식문서를 읽고 금방 작성이 가능할 정도였다.

    공부하면서 봐온 문서 중 가장 정리가 잘 되어있다 생각했다!
    https://docs.cypress.io/guides/overview/why-cypress

  2. Cypress UI는 첫 테스트 코드 작성에 생길 수 있는 많은 이슈를 해결해 주었다. 원하는 Element를 가져오는 경우와 테스트가 실패하는 이유를 제공된 UI로 확인이 가능한 게 매우 편리했다.

    내가 원하는 요소를 잘 클릭하는지 위 사진처럼 쉽게 알 수 있다!!

  3. 비동기 처리를 하지 않아 편리했고, 안정성이 높았다.

    안정성? 기존 E2E 테스트는 UI 렌더링 되는 시점이 제어가 잘 안 되어 불안정한 테스트 결과를 도출했다.
    이를 해결하기 위해 Cypress는 큐 구조에 실행코드를 미리 적재한 후 내부엔진으로 비동기 처리하여 렌더링 된 이후 테스트가 진행되어 테스트 결과에서 안정성이 높다.

  4. 단일 탭, 단일 URL 제한으로, 제공된 시나리오 모두를 테스트할 수 없었다.

    SSO 관련해 테스트 코드를 작성할 때 서비스를 클릭해 생성된 탭을 확인하기 어려웠다.
    invoke라는 메서드로 a 태그에 있는 target 옵션을 제거해 현재 탭에서 새 URL이 열리게 한 후에 테스트 진행이 가능하지만. 테스트 후 돌아가는 과정이 불안정하여 테스트 코드로 작성하지 않았다.

Testcafe

  1. 멀티 탭 지원으로 다양한 테스트가 가능했다.

    하지만 SSO 특성상 여러 URL을 거쳐 최종 페이지에 도달하는 과정에서 안정된 테스트 결과를 도출하려면 많은 시간을 기다리게 해야 하는데 이는 테스트 코드에 적합하지 않다고 생각된다. (PC 사양마다 최종 페이지 도달까지의 걸리는 시간이 달랐다)

  2. 테스트 코드를 재실행 까지 15초 남짓 시간이 들어 불편했다.

    TestCafe 또한 설정에 Live 모드를 켜 코드 수정 시마다 테스트를 재 시작이 가능하지만, 가끔 CSS가 깨져 원활한 테스트 진행이 잘되지 않아 새로고침 하며 디버깅을 진행해 불편했다.

  3. Selector 메서드중 유용하게 사용한 withText가 있지만, 체인이 되지않아 Cypress contains보다 사용에 불편했다.
    Cypress contains vs Testcafe withText (두 기능 모두 특정 문자를 가지고 있는 요소를 가져온다)
    // 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('제출'));

결론

  1. Cypress를 선택했다. 테스트 작성 시 안정성이 높았고, Mocha, chai 라이브러리 기반으로 이루어져 상대적으로 간편했다. (Mocha, chai를 이전에 경험했었다)
  2. 테스트 케이스를 억지로 성공시키는건 올바른 방법이 아니라고 판단하여 Testcafe의 장점인 멀티 탭 제공이 크게 와닿지 않았다.
  3. 테스트코드를 작성할 땐 data-cy를 활용하여 테스트 깨짐에 대처가 가능하다.

테스트코드 작성중 마주한 힘들었던 과정

  1. 어떤식으로 테스트가 진행되는지?

    이전에 테스트 코드를 한번 공부해볼까? 생각은 했었지만, 막상 테스트가 어떻게 동작해야 하는 거지? 무엇을 테스트해야 하지? 이 고민이 좀처럼 사라지지 않아 시작하기 어려웠다.
    서비스에 대해 인증받는 과정이라 어떤 부분이 문제인지 대략적인 시나리오가 제공되서 첫 시나리오 작성에 대한 감을 잡기 시작했다.

  2. 내가 생각한 것보다 시간이 많이 들었다

    기본적으로 Assertion 방법, Cypress에서 제공하는 많은 API와 기능 중 내가 테스트하는 서비스에 어떤 것이 적절한지 공부하고 적용하는 과정에서 많은 비용이 들었다.

  3. 개발환경….

    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/


Cypress 기본 기능들

  1. visit : URL 방문, 기본적으로 config 파일에 설정된 baseUrl를 기준으로 이동한다. Cypress는 단일 사이트에서 테스트를 진행해야 하며 다른 사이트 방문이 기본적으로 불가능하다. ex) naver → google = 실패
    다른 사이트로 이동해서 테스트하려면 우회적으로 테스트 코드를 작성해야한다.

  2. get : Selector or alias(별칭)로 하나 이상의 요소를 가져올 수 있다.

  3. contains : 원하는 텍스트를 포함한 요소를 가져올 수 있다.

  4. type : 선택된 요소에 텍스트를 입력을 할 수 있다. ex)검색창에 값 넣기.

  5. click : 선택된 요소에 클릭 이벤트를 발생시킨다.

  6. wait : 일정시간동안 테스트를 멈춘다.

  7. 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/

아쉬웠던 점

  1. APP 코드에 별칭을 지정하여 사용하면 여러 환경에서 동일한 테스트가 가능하지만 이번에 작성한 테스트 코드는 UI가 변경되면 테스트가 종종 실패하는 경우가 생겨 수정 해야하는 비용이 발생했다. (인증을 준비하던 서비스라 수정이 어려웠다)
  2. 해결하지 못한 이슈
    Alert, Confirm테스트 중 메세지가 올바르게 나타나고 있는지 테스트하는 코드를 작성 했는데 작성한 케이스가 가끔씩 Fail이 뜨기 시작했다. 해당 이슈는 API 요청 결과에 따라 성공 or 실패 메세지를 보내주고 있는 APP 코드에서, API 요청은 기다리지만 Alert 메세지를 받기 전 작성해둔 테스트와 비교하여 Fail이 나오는 상황이다. 결국... 해당 이슈는 0.3초에 시간을 대기하여 처리했다.

마치며

테스트 코드를 작성하며 개발자로서 도움이 되었던 부분은 테스트 코드 작성법뿐만 아니라 다양한 도구를 조사하고 사용해보며 정리하는 과정에서 개발자를 위한 도구를 내가 가진 환경에 어떤 도구의 선택이 적합한지 판단하는 시간을 가지는게 생각보다 더욱더 중요한 걸 깨달았다.

profile
공부 후 기록📃

0개의 댓글