테스트 코드(Test Code)

윤태규·2024년 1월 19일

01. 테스트 코드에 대해 알아보기

  • 1) 테스트 코드가 무엇일까요? 👉 **테스트코드(Test Code)**라는것이 조금은 생소하죠? 이건 여러분이 개발한 코드가 여러분이 의도한대로 동작하는지 작성하는 코드입니다! 내가 스스로 잘했는지 체크하기 위해 만들어두는 체크리스트와 비슷해요! **테스트 코드**란 말 그대로 우리가 작성한 코드에 문제가 없는지 **테스트하기 위해 작성하는 코드**입니다. 흔히 테스트 기본 원칙이라고 불리는 ["일곱 테스트 원칙"](https://www.boxuk.com/insight/the-seven-principles-of-testing/)에서는 첫번째 규칙이 아래와 같습니다. > Testing shows the presence of defects, not their absence **테스팅은 결함이 없는것이 아니라, 결함의 존재를 보여주는것이다.** > 이 원칙을 알아두고 테스트 코드를 작성하는 것이 가장 중요합니다. 왜냐하면 테스트 코드의 목적은 “내 코드가 멀쩡하다!” 라고 증명하기 위한 것이 아니라, "**내 코드가 멀쩡하다면 이렇게 결과가 나와야 한다!**" 라는 기대치를 설정하고 검증하기 위한 것이기 때문이죠. 🙂 하나의 기능을 여러번 수정하다 보면 예상치 못한 결과가 나오기 마련인데요, 테스트 코드를 이용하여 **여러분의 코드를 믿고 사용**할 수 있도록 해보시길 바랍니다. 아래와 같은 글들도 있으니 꼭 읽어보시길 바랍니다. - [https://ssowonny.medium.com/설마-아직도-테스트-코드를-작성-안-하시나요-b54ec61ef91a](https://ssowonny.medium.com/%EC%84%A4%EB%A7%88-%EC%95%84%EC%A7%81%EB%8F%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1-%EC%95%88-%ED%95%98%EC%8B%9C%EB%82%98%EC%9A%94-b54ec61ef91a)
  • 2) 테스트 코드의 종류에 대해 알아보기 테스트 코드란, 어떠한 언어에 얽매이지 않고 개념적으로 표현하기도 합니다. 많은 개발자들이 오랜 기간동안 테스트 코드를 작성하며 아래와 같은 테스트 코드의 종류를 정립했으며, 우리는 이 중 가장 작은 기능을 테스트하는 "단위 테스트" 코드를 실제로 작성해볼 예정입니다 🙂
    • 단위 테스트 (Unit Test): 가장 작은 규모의 기능을 테스트합니다.

    • 통합 테스트 (Integration Test): 다양한 기능을 합쳤을때 생기는 문제를 방지하기 위한 테스트입니다.

    • E2E 테스트 (End-to-end Test): 끝에서 끝(종단 간)을 의미하는 End to end 테스트입니다.
      쉽게 말하면 백엔드부터 시작해서 최종적으로 웹 페이지가 원하는대로 동작하며 원하는 데이터를 잘 보여주는지 확인합니다.

      이외에도 개발자들은 돌연변이 테스트와 같은 다양한 기법들로 자신들의 서비스가 더욱 치밀하고 탄탄한 서비스로 거듭날 수 있도록 노력하고 있습니다 🙂

      💡 번외) 넷플릭스는 자신들의 서버가 아무때나 무작위로 다운되도록 해서 자신들의 인프라에 약점이 있는지 찾아내는것을 도와주는 ["카오스 몽키"라는 솔루션](https://www.itworld.co.kr/news/152680)을 개발해서 사용했습니다. (카오스 엔지니어링)

      언제든 서버가 죽더라도 최대한 빠르게 자동으로 서버를 재구성하는 환경으로 운영할 수 있습니다.

  • 3) 테스팅 프레임워크 Jest에 대해 알아보기 우리가 지금 Node.js를 통해 사용하고 있는 언어인 JavaScript에서 사용이 가능한 테스팅 프레임워크의 종류는 매우 다양한데요,
    이 중 페이스북에서 개발한 테스팅 프레임워크인 Jest를 사용해보도록 하겠습니다!
    • 왜 굳이 Jest를 써야할까요? Jest는 출시한지 얼마 지나지 않아 주목받았는데요, 페이스북에서 개발한 프론트엔드 라이브러리인 React.js와도 궁합이 아주 좋기 때문에 엄청난 성장세를 보이며 2022년 기준 JavaScript 개발자들 사이에서 가장 많이 사용되는 테스팅 프레임워크로 뽑혔습니다.Nest.js 프레임워크에서도 기본 지원하는 테스팅 프레임워크랍니다. 😉 [테스팅 프레임워크 사용률 지표] 파란색이 Jest, 그 외엔 원래 존재하던 테스팅 프레임워크
  출처: [https://2022.stateofjs.com/](https://2022.stateofjs.com/ko-KR/libraries/testing/) [테스팅 프레임워크 사용률 지표] 파란색이 Jest, 그 외엔 원래 존재하던 테스팅 프레임워크
      출처: https://2022.stateofjs.com/
    • 왜 인기가 많은건데요? Jest는 다른 테스팅 프레임워크와 비교하면 여러가지 장점이 있지만, 그 중 가장 제일이라고 생각되는것은 테스트 코드의 표현이 다른 프레임워크보다 훨씬 간결합니다. 그렇기 때문에 여전히 많은 자바스크립트 개발자들에게 사랑받고 있습니다 😉
  • 4) Jest를 실제로 사용해 볼 준비!
  • 1) jest 모듈 설치 이번에도 Jest를 간단하게 사용해보기 위해 새로운 프로젝트를 구성해보겠습니다! 프로젝트로 사용할 폴더를 만들고, 그 폴더를 VS Code로 열어주세요! 그리고 언제나처럼 yarn을 이용하여 모듈을 설치합니다! (공식 문서 참고)
    # yarn 프로젝트를 초기화합니다.
    yarn init -y
    
    # DevDependencies로 jest를 설치합니다.
    yarn add -D jest
  • 2) package.json 파일 수정하기 이 파일을 열어보시면, 아래와 같은 구문이 있을겁니다!
    {
    ...
    	"main": "index.js",
      "license": "MIT",
    ...
    }
    "license": "MIT", 다음 줄에서, 아래의 내용을 복사해주세요!
    {
    ...
      "type": "module",
      "scripts": {
        "test": "node --experimental-vm-modules node_modules/.bin/jest"
      },
    ...
    }
    이렇게 하면, jestyarn test 와 같은 명령어로 실행시킬 수 있습니다. 또한 이렇게 하는것이 모범 사례로 꼽힙니다 😉
  • 3) 테스트 할 간단한 함수 만들어보기 프로젝트 폴더 안에, validation.js 라는 이름으로 파일을 하나 생성해주세요! 그리고 아래의 코드 스니펫을 복사해 validation.js 파일에 붙여넣고 저장해주세요!
    • [코드스니펫] 이메일 검증 함수 기반 코드
      // validation.js
      
      export const isEmail = (value) => {
        // value가 이메일 형식에 맞으면 true, 형식에 맞지 않으면 false를 return 하도록 구현해보세요
        return false;
      };

02. Jest로 간단한 단위 테스트 코드 작성해보기 (1)

  • 1) 단위 테스트 코드 파일 생성 🔥 이번 시간은 **jest**를 사용해보기 위해 구성했던 프로젝트에 본격적으로 코드를 적어보는 시간입니다! 자, 이제 테스트 코드를 작성할 파일을 생성할건데요! 일반적인 관례로 `테스트할파일이름.spec.js` 와 같은 형식으로 파일을 만듭니다. **jest** 또한 위 형식의 이름을 가진 파일을 읽어들여 테스트 코드를 실행하는게 기본 설정이기도 합니다. 그러면 우리는 `validation.spec.js` 라는 이름으로 만들면 되겠죠? 만들어주세요!
  • 2) 단위 테스트 코드 작성 테스트 파일은 만들었는데, 코드를 어떻게 작성할지 막막한가요? 걱정마세요 😉 jest가 테스팅을 위해 기본적으로 제공하는 함수들이 있는데요, 지금 우리가 자주 쓸 함수는 아래와 같습니다!
    • test(): 단위 테스트를 묶어주는 함수입니다.

    • expect(): 특정 값이 만족되는지(정상적인지) 확인하기 위한 표현식을 작성할수 있게 해주는 함수입니다.

      이 설명만 보면 잘 모르겠죠? 아래 예시를 참고해주세요!

    • [코드스니펫] 테스트코드 예시

      ```jsx
      // validation.spec.js
      
      import { isEmail } from './validation';
      
      test('테스트가 성공하는 상황', () => {
        expect(isEmail('이메일이 아니에요')).toEqual(false);
      });
      
      test('테스트가 실패하는 상황', () => {
        expect(isEmail('my-email@domain.com')).toEqual(true);
      });
      ```

      위 예시를 validation.spec.js 에 붙여넣어 저장한 뒤, yarn test 명령어를 실행했을때 아래와 같이 뜨면 정상적인 상황입니다!

      스크린샷 2023-08-09 오전 12.32.28.png

  • 3) 단위 테스트 코드 요구사항 ❓ 요구사항 1개마다 test 함수를 1개씩 만들고, 그 안에 테스트 코드를 작성해주세요! - 요구 사항 - 입력한 이메일 주소에는 "@" 문자가 1개만 있어야 이메일 형식이다. - 입력한 이메일 주소에 공백(스페이스)이 존재하면 이메일 형식이 아니다. - 입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다. - **[코드스니펫] 연습 문제 단위 테스트 코드 템플릿** ```jsx // validation.spec.js import { isEmail } from './validation'; test('입력한 이메일 주소에는 "@" 문자가 1개만 있어야 이메일 형식이다.', () => { // 여기에 테스트코드를 채워주세요! }); test("입력한 이메일 주소에 공백(스페이스)이 존재하면 이메일 형식이 아니다.", () => { // 여기에 테스트코드를 채워주세요! }); test("입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다.", () => { // 여기에 테스트코드를 채워주세요! }); ``` 🔥 **위 요구사항에 해당하는 테스트 코드를 먼저 작성해주세요! 테스트가 실패해도 절대로 `isEmail` 함수를 먼저 수정하지 말아주세요!!** ### 연습 문제 답안 - `단위 테스트 코드 연습 문제` 답안 ```jsx // validation.spec.js import { isEmail } from './validation'; test('입력한 이메일 주소에는 "@" 문자가 1개만 있어야 이메일 형식이다.', () => { expect(isEmail("my-email@domain.com")).toEqual(true); // 1개만 있는 상황 expect(isEmail("my-email@@@@domain.com")).toEqual(false); // 여러개 있는 상황 expect(isEmail("my-emaildomain.com")).toEqual(false); // 하나도 없는 상황 }); test("입력한 이메일 주소에 공백(스페이스)이 존재하면 이메일 형식이 아니다.", () => { expect(isEmail("myemail@domain.com")).toEqual(true); expect(isEmail("my email@domain.com")).toEqual(false); }); test("입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다.", () => { expect(isEmail("e-m-a-i-l@domain.com")).toEqual(true); expect(isEmail("-email@domain.com")).toEqual(false); }); ```
  • 4) 테스트 코드 실행 요구사항을 테스트 코드로 잘 표현해보셨나요?
    이번에도 똑같이 yarn test 명령어를 이용해 테스트 코드를 실행해보세요! 스크린샷 2023-08-09 오전 12.37.11.png 위 사진처럼 failed가 있도록. 즉, 실패하는 테스트가 최소 하나라도 있도록 작성해주세요!! 저희가 항상 falsereturn 하도록 구현해둔 함수 때문이긴 하지만 저희는 지금부터 테스트 코드를 통해 여러분의 코드에 결함이 있다는것을 스스로 증명할 수 있게 되었어요! 🎉 개발자는 항상 모든걸 확인하고 증명하는 역할임을 잊지 마세요! 💪 만약 항상 모든 코드가 성공한다면 지금 작성한 테스트 코드에 **빈틈이 많으며**, 자신의 코드의 **결함을 찾지 못하는 테스트 코드**를 작성하신거랍니다! 🥲
  • 5) 테스트 코드가 통과하도록 isEmail 함수 코드 디버깅
    실패하는 테스트가 있었나요? 만약 없었다면 다시 위로 올라가서 요구사항을 만족시키며 테스트가 통과하는 내용이 있도록 작성해보세요!
    그리고 실패하는 테스트가 있었다면 이제 테스트가 통과할 수 있도록 isEmail 함수를 수정해볼건데요, 이렇게 자신의 코드에 문제(Bug)가 있는것을 찾아서 증명하고, 문제(Bug)를 고치는 행위를 디버깅(Debugging)이라 부릅니다!
    그럼 isEmail 함수가 제대로 동작할 수 있도록 디버깅해주세요!
    함수를 제대로 고쳤다고 판단하면 테스트 코드를 실행해서 테스트가 모두 통과하는지 보는것입니다!
    isEmail 함수 디버깅 예시
    // validation.js

export const isEmail = (value) => {
const email = (value || '');

if (email.split('@').length !== 2) {
return false;
} else if (email.includes(' ')) {
return false;
} else if (email[0] === '-') {
return false;
}

return true;
};

03. Jest로 간단한 단위 테스트 코드 작성해보기 (2)

  • 1) 테스트 코드가 실패할 수 있도록 단위 테스트 코드 추가 작성 👉 여러분이 작성한 코드가 잘 동작하는것을 확인하셨나요? **그런데 갑자기 기획자가 이메일 주소 양식을 변경하고 싶다고 하는 상황이 발생해버렸어요! 😱**

추가된 요구사항은 아래와 같습니다.

  • ✅ 추가 요구사항
    • 입력한 이메일 주소중, **로컬 파트(골뱅이 기준 앞부분)에는 영문 대소문자숫자, 특수문자덧셈기호(+), 하이픈(-), 언더바(_)** 3개 외에 다른 값이 존재하면 이메일 형식이 아니다.
    • 입력한 이메일 주소중, **도메인(골뱅이 기준 뒷부분)에는 영문 대소문자숫자, 점(.), 하이픈(-)** 외에 다른 값이 존재하면 이메일 형식이 아니다.

여러분은 다행히 테스트 코드를 작성해두었기 때문에 지금 구현해둔 함수가 위 요구사항에 맞게 동작하는지 쉽게 확인할 수 있어요!!
그러면, 위 요구사항을 만족시키도록 테스트 코드를 추가로 작성해주세요!

  • 추가 요구사항을 만족시키는 테스트 코드 예시
    // validation.spec.js

...
test("입력한 이메일 주소중, 로컬 파트(골뱅이 기준 앞부분)에는 영문 대소문자와 숫자, 특수문자는 덧셈기호(+), 하이픈(-), 언더바(_) 3개 외에 다른 값이 존재하면 이메일 형식이 아니다.", () => {
expect(isEmail('_good-Email+test99@domain.com')).toEqual(true);
expect(isEmail('my$bad-Email9999@domain.com')).toEqual(false);
});

test("입력한 이메일 주소중, 도메인(골뱅이 기준 뒷부분)에는 영문 대소문자와 숫자, 하이픈(-) 외에 다른 값이 존재하면 이메일 형식이 아니다.", () => {
expect(isEmail('my-email@my-Domain99.com')).toEqual(true);
expect(isEmail('my-email@my_Domain99.com')).toEqual(false);
expect(isEmail('my-email@my$Domain99.com')).toEqual(false);
});
2) isEmail 함수 다시 디버깅
추가 요구사항에 대한 isEmail 함수 디버깅 예시
// validation.js

export const isEmail = (value) => {
const email = value || "";
const [localPart, domain, ...etc] = email.split("@");

if (!localPart || !domain || etc.length) {
return false;
} else if (email.includes(" ")) {
return false;
} else if (email[0] === "-") {
return false;
}

for (const word of localPart.toLowerCase().split("")) {
if (!["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","-","+","_"].includes(word)) {
return false;
}
}

for (const word of domain.toLowerCase().split("")) {
if (!["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","-", "."].includes(word)) {
return false;
}
}

return true;
};
정규 표현식을 이용한 isEmail 함수는 어떻게 만들까요?
// validation.js

export const isEmail = (value) => {
const email = value || "";
const [localPart, domain, ...etc] = email.split("@");

if (!localPart || !domain || etc.length) {
return false;
} else if (email.includes(" ")) {
return false;
} else if (email[0] === "-") {
return false;
} else if (!/^[a-z0-9+_-]+/gi.test(localPart))returnfalse;elseif(!/[az09.]+/gi.test(localPart)) { return false; } else if (!/^[a-z0-9.-]+/gi.test(domain)) {
return false;
}

return true;
};

profile
끝까지 가자

0개의 댓글