[포스코x코딩온] KDT-Web-8 8주차 회고3 테스트코드 입문

Yunes·2023년 8월 25일
0

[포스코x코딩온]

목록 보기
24/47
post-thumbnail

Manual Testing 과 Automated Testing

Manual Testing(수동 테스트)Automated Testing(자동 테스트)
지루하고 번거로움초기 테스트를 적을 노력이 필요하나 그 이후엔 필요 없다
오류가 발생하기 쉽다예측 가능하고 지속적이다
종종 불완전하다 (모든 상황에 대해 처리하지 못할 수 있다)높은 완성도로 많은 상황에 대해 처리할 수 있다

Unit Test, Integration Test, E2E Test

unit 은 앱에서 함수, 클래스, 컴포넌트 등에 해당하는 가장 작은 블럭을 만드는 것을 의미한다. 보통은 함수가 unit 에 해당한다. 애플리케이션은 보통 이러한 unit 들의 결합으로 구성된다.

즉, 단위(unit) 테스트 는 테스트 가능한 가장 작은 소프트웨어를 실행해 예상대로 동작하는지 확인하는 테스트를 말한다.

  • Jest, Mocha, Jasmine

애플리케이션이 테스트들의 결합으로 구성되므로 모든 unit 들이 테스트된다면 전반적인 앱은 반드시 동작해야 한다. 이러한 테스트는 Integration 테스트라 불리는 통합 테스트로 더 큰 범위를 검증할 수 있다.

unit test 는 전체 흐름을 구성하는 각 세부적인 요소를 테스트한다.


통합 테스트 는 단위 테스트와 달리 외부 라이브러리까지 묶어 검증시 사용한다. 통합 테스트는 단위 테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모아 이들이 의도대로 동작하는지 확인하는 테스트이다.

통합(Integration) 테스트 는 API, 여러 모듈, 서로 영향을 미치는 단위에 대해 의도에 맞게 상호 작용하는지 테스트하는 것을 말한다.

  • SuperTest, Nock

  • 통합 테스트 는 unit 블럭들의 결합을 테스트한다.

  • 통합 테스트 는 unit 블럭들이 함께 동작하는지를 검증한다.

  • 통합 테스트 는 API 호출과 응답, 데이터베이스 접근 등을 테스트할때 사용한다.

  • 각 unit 들은 개별적으로 동작하는데 문제가 없어도 결합했을때 에러가 발생할 수도 있다. 통합 테스트는 그런 부분을 확인할 수 있다.

integration test 는 소프트웨어 블럭의 결합을 테스트한다.

Unit 테스트를 하는 이유는 무엇일까

  • 끝나지 않는 수동 테스트를 피할 수 있다.
  • 항상 거의 100% 가까운 코드 & 상황에 대해 테스트를 할 수 있다.
  • 거의 모든 상황에 대해 코드 변화를 테스트할 수 있다.
  • 테스트하기 쉬워지기 위해 더 깨끗하고 나은 코드를 작성할 수 있다.

E2E 테스트란?

Endpoint(종단) 간 테스트. 보통은 Wep, App 등에서 GUI 를 통해 시나리오, 기능 테스트를 수행한다.

E2E 테스트 는 사용자 스토리에 맞춰 수행하고 시나리오상 의도에 맞게 흘러가는지 확인하는 테스트로 사용자 입장에서 테스트하는 것을 의미한다.

  • Puppeteer, Playwright, Cypress

단위 테스트에서 E2E 테스트로 갈수록 검증 대상이 사용자 flow 에 근접하고 그 역으로 갈수록 코드 레벨에 가까워 지는 모습을 보인다.

  • E2E 테스트 는 unit, integration 보다는 사용자의 행동, 앱의 특정 API 인터페이스에 초점을 맞춘 테스트이다.
  • E2E 테스트 는 앱의 기능 ( 예를 들어 이미지 업로드 API 엔드포인트를 만들때 들어오는 요청으로부터 이미지를 추출하고 파일 시스템에 저장하는 전체 흐름 ) 이 전체적으로 잘 동작하는지를 테스트한다.
  • 가장 실질적인 테스트라고 볼 수 있다.

E2E test 는 전체 흐름을 테스트한다.

2023 Trend

이미지 출처 : THE SOFTWARE HOUSE - JavaScript trends in 2023

유닛 테스트 에서는 Jest 가 여전히 우세한 추세이나 1~2년 전만큼 지배적인 비율을 보이지는 않는다.
E2E 테스트 에서는 Cypress 가 가장 높은 비중을 보이나 약간 줄어드는 추세이고 Playwright 에 대한 사용 비중이 높아지고 있는 모습을 볼 수 있다.

이미지 출처 : 2022.stateofjs.com

E2E 테스트에 대한 관심도에서도 Playwright 의 성장세가 뚜렷하다.

위의 트렌드를 볼 때 E2E 테스트를 도입하고자 하지만 못하고 있는 상황을 알 수 있는데 이는 극명한 장단점으로 인한 E2E 테스트 프레임워크 도입이 어렵기 때문이다.

장점

  • 디버깅 비용의 절감
  • 회귀 테스트
  • 화면 기반의 서비스 스펙 문서

단점

  • 러닝 커브
  • 유지보수 비용

회귀 테스트 : 개발, 업데이트 과정에서 소프트웨어의 변경 사항이나 수정 사항이 기존에 정상적으로 작동하던 기능에 영향을 미치지 않았는지 확인하는 테스트

TDD(Test-Driven Development) 란?

테스트 주도 개발 TDD 는 코드를 짤 때 작은 단위의 테스트 코드를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복해 구현한다.

TDD 에서의 반복 사이클
1. 앱의 코드보다 먼저 실패하는 코드를 작성한다
2. 테스트 코드가 성공하도록 하는 실제 코드를 추가한다.
3. refactoring 단계로 중복 코드 제거, 일반화등을 진행한다.

TDD 는 테스트 코드를 작성한 뒤 실제 코드를 작성한다.

Jest 설치 및 실행하기

npm i --save-dev jest supertest

// 동일

npm i -D jest supertest

--save-dev : 설치된 패키지를 프로젝트의 개발 의존성으로 추가하라는 것을 나타낸다.
개발 의존성 : 프로젝트를 개발하는 동안에만 필요한 패키지
--save : 패키지를 프로젝트의 일반 의존성으로 추가. 현재는 --save 옵션을 넣지 않아도 기본적으로 패키지를 일반 의존성으로 설치한다.
-D : --save-dev 와 동일하다.

테스트 실행을 위해 package.json 의 script 를 수정하자

figure
⬇️
npm test
// index.js
const express = require("express");
const app = express();
const PORT = 8000;

//ejs
app.set("view engine", "ejs");
//body-parser
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

//router
app.get("/", (req, res) => {
  res.render("index");
});

//server open
app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

module.exports = app;
// index.test.js
const request = require("supertest"); // For testing HTTP requests
const app = require("./index"); // Assuming your Express app is exported from index.js

// Mock the session middleware for testing
app.use((req, res, next) => {
  req.session = {
    // Mock session data here
  };
  next();
});

describe("Express App", () => {
  it("responds to GET /", async () => {
    const response = await request(app).get("/");
    expect(response.status).toBe(200);
  });

  // Add more test cases for different routes and functionalities
});

Vitest 설치 및 실행하기

npm i -D vitest
figure
⬇️
npm i -D @vitest/ui

위의 패키지를 설치시 describe, test 별 진행 결과를 GUI 형식으로 볼 수 있다. script 의 "test" 를 vitest --ui 로 바꾸면 테스트 결과를 GUI로 보여주는 사이트를 볼 수 있다.

업로드중..

// index.js
import express from "express";
const app = express();
const PORT = 8000;

//ejs
app.set("view engine", "ejs");
//body-parser
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

//router
app.get("/", (req, res) => {
  res.render("index");
});

//server open
app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

export default app;
// index.test.js
import { expect, describe, it } from "vitest";
import request from "supertest"; // For testing HTTP requests
import app from "./index"; // Assuming your Express app is exported from index.js

// Mock the session middleware for testing
app.use((req, res, next) => {
  req.session = {
    // Mock session data here
  };
  next();
});

describe("Express App", () => {
  it("responds to GET /", async () => {
    const response = await request(app).get("/");
    expect(response.status).toBe(200);
  });

  // Add more test cases for different routes and functionalities
});

Jest 를 Unit Test 중 가장 많이 사용하지만 최근들어 사용 비중이 감소하는 추세이며 빠르게 비중을 높이고 있는 Vitest 를 한번 사용해보려 한다.

Jest 는 다소 느린 부분이 있다.

단순하게 위의 test 코드를 jest 와 vitest 로 실행한 결과를 비교해볼까?

코드는 하나도 바뀌지 않았고 그저 jest 에서 vitest를 사용하는 형태로 변환만 해줬다.

jestvitest

같은 코드인데 jest 는 1.375s 가 소요되었는데 vitest 는 95ms 가 소요되었다. vitest 를 쓰지 않을 이유가 보이지 않는다.

Vitest 는 vite 환경으로 테스트를 진행할 수 있도록 도와주는 프레임워크이다. test 수행속도가 jest 보다 확연히 뛰어나다.

또한 Jest 는 ECMAScript 모듈이 실험적으로 지원되고 있어서 import 문법에 용이하지 않고 추가적인 툴을 설치해야 한다.

또한 vite 를 사용시 dependencies 를 최소화할 수 있고 jest 처럼 test 환경을 구성하기 위해 많은 babel, 부가 설정의 세팅이 필요없다.

Vitest 는 Jest 와 문법이 완전히 동일하다.

레퍼런스

docs
jest
vitest
blog
하나몬 - TDD란?
saeeng - VITE + VITEST (migrate from Jest)
reference
테코블 - 단위 테스트, 통합 테스트, 인수 테스트
naver d2 - e2e test with meerkat 조문기_NEDAY 23.07
THE SOFTWARE HOUSE - 2023 JavaScript Trend
albertgao.xyz - express.js 를 jest 와 supertest 로 어떻게 테스트하는가?

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글