ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue.js] Cypress를 이용한 E2E Test
    ■ Front-End/- Vue.js 2019. 12. 10. 19:38

     

     

    지금까지 다양한 언어를 사용해서 개발을 해왔지만, 실제로 테스트 코드를 짜본적은 거의 없다.

    1-2년 전에 API 서버를 만들 때, local 환경에서 JUnit 테스트를 해본 적이 있었는데 이 또한 제대로 테스트를 한거라고 볼 순 없었다.

    말이 테스트지 local DB에 테스트용 데이터를 넣어야할 때, JUnit 코드에서 랜덤 함수가 들어간 로직을 돌린 적도 있었다.

     

    아무튼 이번에 Front에서 테스트 코드를 짜볼 수 있는 기회가 생겼는데,

    크루의 추천을 받아 Cypress를 이용해서 테스트 코드를 작성해 봤다.

     

     

     

    Test 종류


    테스트에는 단위테스트, 통합테스트, 그리고 E2E 테스트로 나눌 수 있다.

    먼저 단위/통합 테스트는 개발자의 관점에서 테스트를 하는 경우를 뜻한다. 서비스 로직을 실행했을 때, 알맞은 데이터를 제공하는지 또는 올바른 로직을 수행하는지를 테스트한다고 볼 수 있다.

     

    E2E 테스트는 아래와 같다.

     

     

     

     

     

     

     

     

     

    Cypress 


    개인적으로 Cypress 사용을 추천하는 이유는(첫 테스트 도구이지만..) 문서 정리도 굉장히 잘 되어있고, 사람들이 많이 사용해서 모르는 부분이 생겼을 때 구글링하기가 굉장히 쉽다! 그리고 GUI로 된 테스트 화면이 있어서 개발자 입장에서 굉장히 보기 편하다. (물론 headless도 지원한다.) 또한 테스트 코드가 JQuery와 비슷한 부분이 많아서 진입 장벽이 좀 낮은 편이다.

     

     

     

    실제 테스트 화면

    GUI로 Cypress를 실행했을 때의 화면이다. 사내 프로젝트여서 가린게 엄청 많다. Cypress 홈페이지에 들어가면 메인 화면에 실행 동영상이 있는데, 그걸 보는걸 추천한다!

     

     

     

    [ 장점 ]

    - 실제 애플리케이션과 테스트 코드를 동일한 브라우저에서 실행해, 빠르고 안정적으로 테스트 가능.

    - 브라우저 기반의 GUI 사용해 테스트 실행 상태를 확인하고 디버깅 가능.

    - 실행된 모든 테스트 명령과 명령이 실행될  UI 상태를 스냅샷 형태로 저장.

    - 전체 테스트 과정을 동영상으로 저장 가능.

    - 크롬 브라우저에서 실행되기 때문에, 개발자 도구를 사용해 디버깅 가능.

     

     

    [ 단점 ]

    - Same-origin 정책을 벗어나는 페이지로 이동 불가능. -> 설정 파일에서 허용할 수 있도록 바꿀 수 있다!

    - Javascript  다른 언어로 테스트 파일을 작성할  없다. -> typescript로 작성 가능하도록 업데이트 된듯?

    -  번에 하나의 테스트만 가능. 동시에   이상의 테스트 불가능! -> 구글링하면 할 수 있는 방법이 있는데 꽤 복잡하다..

     

     

     

     

     

     

    설치 및 실행 방법


    설치

    - npm install cypress

     

    실행 

    - GUI로 실행 : cypress open

    - headless로 실행 : cypress run 또는 cypress open --headless

     

    +) 실무에서는 보통 dev, production 으로 나눠서 빌드/배포 하기 때문에, 환경변수 또한 나눠서 저장해야한다. 개발 환경 별로 나눠서 테스트를 실행하는 방법은 아래와 같다. (만약 브랜치 별로 나눌 필요가 없다면, cypress.json 파일에 설정 값을 넣으면 된다.)

     

     

    1. config 폴더 만들기

    cypress 설치 후, cypress 폴더 아래에 config 폴더를 생성한다. 그리고나서 아래와 같이 원하는 브랜치 별로 json 파일을 만들어준다.

     

    (좌) config 내 json 파일 생성, (우) json 파일 내부

    json 파일에는 환경 변수로 설정할 값들, api를 호출하거나 특정 사이트 이동을 위해 필요한 url을 'baseUrl'로 설정할 수 있다.

    또한, 테스트할 화면 브라우저의 너비와 높이를 설정할 수도 있다.(viewportWidth/viewportHeight)

     

     

     

    2. /plugins/index.js

    브랜치별로 json 파일을 만들었다면, 이제 테스트를 시작할 때 해당 파일을 읽어올 수 있도록 설정해야한다.

    /cypress/plugins/index.js 파일에 아래와 같이 추가한다.

     

    const fs = require('fs-extra');
    const path = require('path');
    
    function getConfigurationByFile (file) {
        const pathToConfigFile = path.resolve('./cypress/', 'config', `${file}.json`);
        return fs.readJson(pathToConfigFile);
    }
    
    // plugins file
    module.exports = (on, config) => {
        // config 아래 json 파일을 읽어오되, 기본 값은 dev.json 파일을 읽어오도록 한다.
        const file = config.env.configFile || 'dev';
        return getConfigurationByFile(file);
    };

     

     

    3. 테스트 실행 명령

    그럼 테스트를 실행할 때, 어떻게 해야 원하는 브랜치로 실행할 수 있을까?

    package.json 파일에서 script 안에 아래와 같이 써준다.

    캡쳐 이미지에는 npm run cy:clean; 도 들어가 있는데, 이는 테스트 파일을 삭제하는 스크립트로 일단은 이 부분은 지우고 써준다.

     

    "cy:report-dev": "cypress run --env configFile=dev"

     

     

     

    참고)

    환경 변수를 사용하고 싶을 때에는 테스트 파일에서 Cypress.env("키") 를 사용하면 된다.

    그런데, 여기서 한 가지 주의할 점은 테스트 파일들이 업데이트 된 환경 변수 값을 공유할 수 없다.

    예를 들면, 환경 변수에 키 값이 "id" 이고, value를 "1"로 설정했다고 하자. 

    1번 테스트 파일에서 Cypress.env("id", "3"); 로 업데이트를 했다. 그 후 2번 테스트 파일에서 Cypress.env("id") 를 가져와서 사용했을 때, 이 값은 무엇일까? 3을 기대했지만, 결과는 1이다. 바뀌지 않는다. 

    이 부분 때문에 엄청 구글링하고 찾아봤는데, 아직까지 cypress에서 해결이 안된 것으로 보인다.ㅠㅠ 웃긴게 GUI모드로 spec all files 했을 땐 공유가 되는데, headless로 테스트 해보면 안된다... 이것 때문에 몇 시간은 잡아먹음 ㅠㅠ

     

     

     

     

     

     

     

     

     

    Sample Code


    프로젝트에 Cypress를 설치하면, 위와 같이 cypress 폴더가 만들어지고 그 아래 여러 파일들이 생성된다.

    그 중 examples 폴더 안에 있는 js 파일에는 종류별로 샘플 코드를 작성하는 방법이 적혀있다. 

    만약 내가 REST API를 호출하는 테스트 코드를 작성하고 싶다면, network_requests.spec.js 파일을 참고하면 된다.

    해당 파일에서도 찾을 수 없는 내용은 cypress 홈페이지 또는 열심히 구글링을 하면 된다 :-)

     

    나는 examples 폴더와 같은 레벨에 프로젝트 테스트용 폴더를 하나 생성했고, 그 안에 테스트 코드를 작성했다.

     

     

     

     

     

    1. REST API 호출 테스트 코드

     

    (1) GET 

    request 메서드를 이용해서 API를 호출했고, 호출 결과 값을 'data' 라는 변수에 저장해서 callback 함수에 사용했다.

     

    여기서 expect(this.data).property('code').to.equal(200); 코드는

    API를 호출한 결과 값(흔히 부르는 resultData)에 code라는 값이 200인지 테스트하겠다는 것이다.

     

     

    (2) POST

    POST 호출시 헤더에 추가할 내용이 있어서 GET과는 조금 다른 방식으로 호출했다.

     

    GET 방식처럼 매개변수에 바로 값을 넣어서 호출하는 방법도 있고, 위처럼 option으로 key:value 형태로 넣어서 호출할 수 있다.

     

    참고 : https://docs.cypress.io/api/commands/request.html#Syntax

     

    request

    Make an HTTP request. Syntaxcy.request(url) cy.request(url, body) cy.request(method, url) cy.request(method, url, body) cy.request(options) Usage Correct Usage cy.request('http://dev.local/seed') Ar

    docs.cypress.io

     

     

     

     

     

    2. 템플릿 화면에서의 클릭 이벤트

     

     

     

    cy.get() 메서드를 사용해서 클릭할 요소의 id 또는 classname을 설정한다.

     

     

     

     

     

    3. Input 요소에 원하는 text 입력하기

    cy.get('#id or .classname').type('입력할 text');

     

    참고 : https://docs.cypress.io/api/commands/type.html#Syntax#article

     

    type

    Type into a DOM element. Syntax.type(text) .type(text, options) Usage Correct Usage cy.get('input').type('Hello, World') // Type 'Hello, World' into the 'input' Incorrect Usage cy.type('Welcome')

    docs.cypress.io

     

    추가

    cypress에서 selector를 좀 더 편리하게 사용할 수 있는 기능이 추가되었다. 개발자 도구를 켜서 클래스명 또는 id를 찾지 않아도, 바로 알 수 있다.

     

     

     

    4. 클릭 및 클릭 후 이벤트 처리

    cy.get('#id or .classname').click().then(function() {

        // 클릭 후 이벤트

    });

     

    참고 : https://docs.cypress.io/api/commands/click.html#Arguments

     

    click

    Click a DOM element. Syntax.click() .click(options) .click(position) .click(position, options) .click(x, y) .click(x, y, options) Usage Correct Usage cy.get('button').click() // Click on but

    docs.cypress.io

     

     

     

     

    5. <ul> 태그 안의 특정 <li> 요소 선택하기

    예)

    <ul class='list_result'>

        <li class='result' >test 1</li>

        <li class='result' >test 2</li>

        <li class='result' >test 3</li>

        <li class='result' >test 4</li>

    </ul>

     

    cy.get('.list_result').eq(0).click();

     

    참고 : https://docs.cypress.io/api/commands/eq.html#Syntax#article

     

    eq

    Get A DOM element at a specific index in an array of elements. The querying behavior of this command matches exactly how .eq() works in jQuery. Its behavior is also similar to that of the CSS pseudo-c

    docs.cypress.io

     

     

     

     

     

    6. element 의 text 값 가져오기

    cy.get('#loginForm')
          .find('.page-desc')
          .then(($title) => {
            const title = $title.text()
            cy.log("title :", title);
            expect(title).not.to.eq('')
        })
    

     

    위 예제는 #loginForm 안에서 className이 page-desc 인 요소의 text 값을 추출해서 공백인지 아닌지 판별하는 테스트이다.

     

     

     

     

     

     

     

     

    Reporters


    GUI 또는 Headless로 테스트를 하면, 테스트 결과는 아래와 같다.

     

     

    GUI 테스트 결과
    headless 테스트 결과

     

    그런데 이런 형식 말고, 다른 형식으로 결과를 보고 싶을 때 어떻게 해야할까? 바로 Reporters 기능을 이용하면 된다.

    일반적으로 Cypress 에서 제공하고 있는 Reporters Tool은 Mocha와 JUnit이 있다.

    Mocha의 경우, 테스트 결과를 json 파일 또는 html 파일로 저장할 수 있다. JUnit의 경우 xml 파일로 저장할 수 있다.

     

     

    1. module 설치하기

     

    // junit 사용할 경우

    > npm install cypress-multi-reporters

     

    // mocha를 사용할 경우

    > npm install cypress-multi-reporters

    > npm install mocha

    > npm install mochawesome

     

     

    2. config 파일 또는 cypress.json에 Report 옵션 설정하기

     

    // junit 사용할 경우

    "reporter": "junit",
    "reporterOptions": {
        "mochaFile": "cypress/test-results/test-[hash].xml",
        "toConsole": true
    }

     

    // mocha 사용할 경우

    "reporter": "cypress-multi-reporters",
    "reporterOptions": {
        "configFile": "reporterOptions.json",
    }

     

    -> mocha를 사용할 경우, cypress.json과 같은 레벨에 reporterOptions.json 파일을 만들고 아래와 같이 적는다.

    {
        "reporterEnabled": "mochawesome",
        "mochawesomeReporterOptions": {
        "reportDir": "cypress/reports/mocha",     // report 결과를 저장할 경로
            "quiet": true,
           "overwrite": true,
            "html": true,   // report 결과를 html 파일로 생성할 것인지.
           "json": true    // report 결과를 json 파일로 생성할 것인지.
        }
    }

     

    3. 테스트 실행

    cypress run 을 실행하면, 테스트 결과가 2번에서 설정한 경로에 저장된다.

    command 창에 cypress run을 바로 입력하면 command not founded 가 나올 때가 있는데, 이 때 package.json에 스크립트를 하나 만들어서 스크립트 명령어로 쳐주면 실행된다.

     

     

     

    junit reporting 결과

     

     

     

     

     

    mocha - html reporting 결과

     

     

     

     

    mocha - json reporting 결과

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    참고 사이트

    https://www.cypress.io/ 

     

    JavaScript End to End Testing Framework

    Fast, easy and reliable testing for anything that runs in a browser. Install Cypress in seconds and take the pain out of front-end testing.

    www.cypress.io

    https://ui.toast.com/fe-guide/ko_TEST/#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%9F%AC%EB%84%88

     

    테스트

    자바스크립트는 최근 몇 년간 비약적인 발전을 통해 사용 범위를 넓혀오고 있으며, 프론트엔드 환경에서 요구하는 애플리케이션의 수준도 나날이 복잡해지고 있다. 이와 더불어 자바스크립트의 테스트 환경도 짧은 기간 동안 많은 변화를 겪었는데, 특히 Node.js의 등장 이후 무수히 많은 도구가 쏟아져 나오며 빠른 속도로 발전해오고 있다.

    ui.toast.com

     

Designed by Tistory.