-
Cypress로 BDD 테스트 코드 작성하기 👍Web/자바스크립트 2021. 2. 7. 23:11반응형
■ Cypress 는 'End-to-End 테스팅'을 위한 프레임워크이다.
End-to-End는 쉽게 말해서, 종단(Endpoint)간 테스트, 즉 사용자의 입장에서 테스트하는 것을 의미한다.
■ npm 혹은 yarn을 이용해서 여느 프레임워크처럼 쉽게 설치할 수 있다.
나는 원래 npm만 이용했는데 yarn이 좀 더 안정적이고 속도도 잘나온다는 소리가 있길래.
어차피 사용법은 비슷하니까, 사용하고 싶은 거 아무거나 골라서 사용하면 될 듯.
참고로, npm과 yarn을 하나의 프로젝트에서 같이 쓰면 충돌이 일어날 수 있다고 하니 조심하자. 🚫
npm install cypress --save-dev yarn add cypress --save-dev
설치를 하고 나면, 특별한 설정이 필요없이 다음의 명령어를 입력해주면 된다.
npx cypress open yarn run cypress open
■ 처음 실행하게되면 프로젝트 폴더에 'cypress'라는 폴더가 생성된다.
해당 폴더 안에 있는 'integration/examples' 디렉토리에 들어가면 다양한 샘플 코드들이 들어있다.
처음에 어떤 식으로 테스트 코드를 작성해야 하는지 모르겠으면 샘플 파일을 참고하도록 하자.
그리고 이제 직접 테스트 코드를 작성하면 되는데, 위치는 integration 폴더로 잡는 것이 좋다.
'integration/examples' 폴더는 샘플 코드만 들어있게 하는 것이 정석이라고 한다.
참고로, eslint를 사용하고 있다면 .eslintrc에 다음의 한 줄을 추가해야 한다.
"extends": ["plugin:cypress/recommended"]
요즘, 여기저기서 TDD라는 말을 많이 들을 수 있다.
TDD는 Test-Driven Development의 약자로, 테스트에 의해 주도되는 개발 과정을 의미한다.
개발자는 먼저 요구사항을 검증하는 자동화된 테스트 케이스를 개발하고,
그 테스트 케이스를 통과하기 위한 최소한의 코드를 작성해주는 일련의 과정을 계속해서 반복하는 것이다.
(위키백과 ㅎ)
쉽게 말해, 실패하는 테스트를 먼저 작성하고나서 해당 테스트를 통과하도록 메소드를 구현해주면 된다.
■ 일단, 요구사항을 반영한 테스트 코드를 작성해보자.
import Stock from '../../src/js/Stock.js'; describe("HTS 기능", () => { const stock = new Stock(); it("주식 매수", () => { assert.equal(stock.buy('삼성전자', 89000, 20), '매수 체결'); }); it("신용 매수 방지", () => { assert.equal(stock.buy('삼성전자', 89000, 100), '신용 매수는 안됩니다!'); }); });
assert는 대충 영어로 번역하면 '(해당 문장이) 사실임을 주장' 한다는 뜻 정도로 해석할 수 있다.
즉, stock 객체의 buy 메소드에 '삼성전자'와 89000, 20을 인자로 넣었을 때
결과가 '매수 체결'이랑 동일해야한다고 주장하는 테스트 코드인 것이다.
■ 이렇게 코드를 작성하고나서 테스트를 돌리면 당연히 오류가 난다.
아직까지 로직을 아무것도 작성하지 않았기 때문에.
이제, 위의 테스트 코드가 잘 작동하도록 그에 맞게 로직을 짜면 된다.
export default function Stock() { const money = 2000000; this.buy = (name, price, share) => { if (price * share <= money) { return '매수 체결'; } return '신용 매수는 안됩니다!'; } }
이렇게 해주면, 위에서 작성했던 테스트 코드가 잘 돌아가도록 만들어줄 수 있다.
TDD는 이런 식으로 전개되는 개발 과정이다.
■ 하지만, 위의 코드들을 잘 보면 사용자의 행동에 관한 부분이 결여되어있다는 것을 알 수 있다.
백엔드만 생각하거나 UI/UX를 전혀 고려하지 않아도 되는 상황이라면 상관이 없겠지만,
프론트엔드를 개발하는 입장에서는 사실, 위의 코드로는 많은 것들을 테스트할 순 없다. 😥
■ 이러한 부분을 해결하기 위해 '댄 노스'라는 인상 좋은 아저씨가 'BDD(Behavior-Driven Development)'를 만들어냈다.
사용자 행위를 중심으로 'GWT'라는 하나의 템플릿을 만들고, 그에 따른 테스트 코드를 작성하게끔 한 것.
GWT는 'Given(환경이 주어지고) - When(사용자의 이벤트가 발생하면) - Then(그에 대한 결과를 보장)'이라는 뜻.
예를 들면,
Given - 유저에게 주식 매수 화면이 렌더링 된다.
When - 유저가 input에 값을 입력하고 매수 버튼을 누른다.
Then - 매수 결과가 화면에 보여진다.
이런 식.
그리고 이 때는 이벤트에 대한 기대 행위가 존재하기 때문에,
그에 맞게 'should'라는 이름의 테스트 메소드를 사용하기 시작했다.
■ If(kakao) 의 설명에 따르면, 계산기를 만든다고 가정했을 때
TDD/BDD의 차이점은 다음과 같다.
■ 이제, BDD를 기반으로 사용자의 행위까지 고려한 테스트 코드를 작성해보도록 하자.
import Stock from '../../src/js/Stock.js'; describe('HTS 기능', () => { before(() => { cy.visit('http://localhost:5000'); }); it('사용자가 처음에 페이지에 접속하면, input들이 전부 비워져 있다.', () => { cy.get('#name').should('have.value', ''); cy.get('#price').should('have.value', ''); cy.get('#share').should('have.value', ''); }); it('사용자가 주식 매수 버튼을 누른다', () => { cy.get('.buttons').contains('매수').click(); cy.get('#notice').should('have.text', '매수 체결'); }); });
before 은 모든 테스트를 시작하기 전에, 초기화하는 코드를 넣는 메소드이다.
여기서는 로컬 서버 페이지에 접속(visit)하는 코드를 집어넣었다.
각 테스트 이전에 실행되는 beforeEach라는 메소드도 있다.
should문은 위에서 언급했듯이, 특정 이벤트에 대해 기대되는 결과를 작성할 때 사용한다.
첫 번째 인자로 'have.value'가 들어가면, 해당 태그의 value가 '두 번째 인자'의 값을 가져야 한다는 뜻이다.
유사한 의미로, 'have.text'는 해당 태그 안에 있는 텍스트가 두 번째 인자의 값을 가져야 한다는 뜻.
(innerText를 생각하면 쉽다)
contains('매수')는, '매수'가 포함된 텍스트를 가지는 엘리먼트를 가져오는 기능.
■ 이런 식으로 사용자의 행위까지 고려해서 테스트 코드를 작성하고 나면
역시나 당연하게도 테스트를 통과하지 못하니, 해당 테스트를 통과하게끔 코드를 작성해주면 된다.
export default function Stock() { const money = 2000000; this.buy = (name, price, share) => { if (price * share <= money) { return '매수 체결'; } return '신용 매수는 안됩니다!'; }; document.querySelector('.buttons').addEventListener('click', e => { const tradeType = e.target.innerText; if (tradeType === '매수') { // 예시니까 매수 버튼 누르면 바로 체결되는걸로..ㅎ document.querySelector('#notice').innerText = '매수 체결'; } }); }
대충 이런 식으로 테스트 코드를 작성하면 되는 것 같다.
이제 막 개념을 알고 공부하기 시작했기 때문에 틀린 내용이 있을 수도 있지만..
보고 들은 내용을 잊지 않기 위해 두려움을 무릅쓰고 포스팅을.. 😋
사실 이전까지는 테스트 코드에 대한 필요성을 크게 느끼지 못해서 사용하지 않고 있었지만
반 강제적으로 Cypress를 사용해야하는 일이 오면서 그 장점을 제대로 깨닫게 되었다.
일단, 테스트 코드의 input을 매번 랜덤으로 입력하도록 코드를 작성해놓으니
전혀 생각지도 못했던 엣지 케이스를 찾을 수 있었다.
또한, 미리 작성해야 할 기능을 테스트 코드로 먼저 작성한 후에 로직을 구현하기 때문에
지금 당장 내가 어느 방향으로 나아가야하는지가 너무 명확해져서
방향을 잃고 헤매는 일이 좀 줄어드는 것 같았다.
그리고 무엇보다도, 리팩토링할 때가 너무 편했다.
수동으로 테스트할 때는 리팩토링 하고 나서, 이전에 만족했던 케이스들을
전부 무사히 만족하는지 일일이 다시 확인해줘야 했지만
테스트 코드를 한 번 작성해놓으니,
리팩토링을 하고 나서도 그냥 테스트 코드 한 번 쓱 돌려주면 검사가 완료되었다.
이 부분에서 가장 큰 감동을 받았던 기억이.. 😂
참고할 만한 내용
If(kakao) 'kotest가 있다면 TDD 묻고 BDD로 가!' 동영상
TOAST Meetup: 실용적인 프론트엔드 테스트 전략(3)
반응형'Web > 자바스크립트' 카테고리의 다른 글
비동기 자바스크립트 (0) 2021.02.20 자바스크립트의 this (0) 2021.02.13 From jQuery to VanillaJS (0) 2020.12.28 ES Lint + Prettier 도입하기 🔥🔥🔥 (0) 2020.12.04 배열의 중복 다루기 (0) 2020.10.30