테스트 코드란 작성한 코드가 문제가 없는지 테스트하기 위해 작성하는 코드.
내 코드가 멀쩡하다!
를 증명하기 위해서 작성하는 것이 아니라 코드의 결과를 확인하고 검증하기 위해 작성한다.단위 테스트(Unit Test)
통합 테스트(Integration Test)
E2E 테스트(End-to-end Test)
페이스북에서 개발했으며 react
와 궁합이 잘 맞아 급격하게 성장한 테스팅 프레임워크이다.
Nest.js
에서 기본 지원하는 테스팅 프레임워크이다.
Jest
는 테스트 코드의 표현이 다른 프레임워크 보다 훨씬 간결하다. -> 빠르게 작성하고 빠르게 테스트 가능하다.
사용 등록: yarn test
package.json
...
"type": "module",
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest"
},
...
관례상 테스트할파일이름.spec.js <- 형식으로 파일명을 작성한다.
test()
단위 테스트를 묶어주는 함수.
expect()
특정 값이 만족되는지 확인하기 위한 표현식을 작성할 수 있게 해주는 함수.
export const isEmail = (value = "") => {
const [local, domain] = value.split("@");
};
if (!value || value.split("@").length !== 2) return false;
if (value.includes(" ")) return false;
if (value[0] === "-") return false;
if (!/^[a-zA-Z0-9+-_.]+$/.test(local)) return false;
if (!/^[a-zA-Z0-9.-]+$/.test(domain)) return false;
test("입력한 이메일 주소에는 @ 문자가 1개만 있어야 이메일 형식이다.", () => {
expect(isEmail("myemail@domain.com")).toEqual(true);
expect(isEmail("myemail@@domain.com")).toEqual(false);
expect(isEmail("@myemail@domain.com")).toEqual(false);
expect(isEmail("my@email@domain.com")).toEqual(false);
expect(isEmail("myemail@doma@in.com")).toEqual(false);
expect(isEmail("myemail@domain.com@")).toEqual(false);
});
test("입력한 이메일 주소에 공백(이 존재하면 이메일 형식이 아니다.", () => {
expect(isEmail("myemail@domain.com")).toEqual(true);
expect(isEmail("my email@domain.com")).toEqual(false);
expect(isEmail("myemail@do main.com")).toEqual(false);
expect(isEmail("myemail@domain. com")).toEqual(false);
});
test("입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다.", () => {
expect(isEmail("myemail@domain.com")).toEqual(true);
expect(isEmail("myemail@doma-in.com")).toEqual(true);
expect(isEmail("-myemail@domain.com")).toEqual(false);
expect(isEmail("myem--ail@domain.com")).toEqual(true);
expect(isEmail("my-email@domain.com")).toEqual(true);
});
test("입력한 이메일 주소 중 로컬 파트(골뱅이 기준 앞부분)에는 영문 대소문자와 숫자, 특수문자는 덧셈기호(+), 하이픈(-), 언더바(_) 3개 외에는 다른 값이 존재하면 이메일 형식이 아니다.", () => {
expect(isEmail("myemail@domain.com")).toEqual(true);
expect(isEmail("my+-Emai_l@domain.com")).toEqual(true);
expect(isEmail("my^^e%m&a!il@domain.com")).toEqual(false);
expect(isEmail("mye+_*^&)mail@domain.com")).toEqual(false);
expect(isEmail("mye#!?>L:mail@domain.com")).toEqual(false);
});
test("입력한 이메일 주소 중 도메인(골뱅이 기준 뒷부분)에는 영문 대소문자와 숫자, 점(.), 하이픈(-)외에 다른 값이 존재하면 이메일 형식이 아니다.", () => {
expect(isEmail("myemail@domain.com")).toEqual(true);
expect(isEmail("myemail@doMa-in.com")).toEqual(true);
expect(isEmail("myemail@d-o231ma-in.com")).toEqual(true);
expect(isEmail("myemail@doma^!#in.com")).toEqual(false);
expect(isEmail("myemail@doma>?in.com")).toEqual(false);
});
PASS ./validation.spec.js
√ 입력한 이메일 주소에는 @ 문자가 1개만 있어야 이메일 형식이다. (3 ms)
√ 입력한 이메일 주소에 공백(이 존재하면 이메일 형식이 아니다. (1 ms)
√ 입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다. (1 ms)
√ 입력한 이메일 주소 중 로컬 파트(골뱅이 기준 앞부분)에는 영문 대소문자와 숫자, 특수문자는 덧셈기호(+), 하이픈(-), 언더바(_) 3개 외에는 다른 값이 존재하
면 이메일 형식이 아니다. (1 ms)
√ 입력한 이메일 주소 중 도메인(골뱅이 기준 뒷부분)에는 영문 대소문자와 숫자, 점(.), 하이픈(-)외에 다른 값이 존재하면 이메일 형식이 아니다. (1 ms)
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 0.667 s, estimated 1 s
Ran all test suites.
--forceExit
app
객체와 Prisma 연결이 Connect
상태로 남아있어 테스트 코드가 종료되지 않을 때 사용한다.--silent
console.log
와 같은 메시지를 출력하지 않는다.--coverage
--verbose
.mockReturnValue(value)
.toBe(value)
.toEqual(value)
.toMatch(regexp | string)
.toBeTruthy()
true
인지 검증한다..toBeInstanceOf(Class)
.toHaveProperty(keyPath, value?)
.toMatchObject(object)
afterAll(fn, timeout)
모든 test()
가 완료된 이후에 수행된다.
테스트가 완료된 이후 DB에 변경된 데이터를 삭제하거나 Mock을 초기화 하기 위해 사용된다.
afterEach(fn, timeout)
각 test()
가 완료된 이후에 수행된다.
테스트코드가 완료된 이후 Mock 또는 변경된 전역 변수를 초기화할 때 사용된다.
beforeAll(fn, timeout)
테스트 코드가 실행되기 전 최초로 수행된다.
DB의 데이터를 초기화하거나 전역 Mock을 초기화할 때 사용된다.
beforeEach(fn, timeout)
각 test()
가 실행되기 전에 수행된다.
테스트가 실행되기 전, 동일한 설정을 반복해야할 때 사용된다.
Mock은 특정 메소드나 함수를 Mocking하기 위해 사용된다.
테스트에서 시간 또는 비용이 많이 들거나, 의존성이 높은 코드를 직접 실행하지 않고, 호출 여부, 입력한 값의 일치 여부 등을 확인하기 위한 가짜 객체이다.
테스트를 하기 위해 매번 DB에 접근하여 데이터를 수정하거나 전송하는 등 비용이 발생하는 작업을 반복할 수 없다. 문제가 발생하거나 로직을 검사하는데 방해가 되는 코드를 실제로 실행한 것 처럼 만들기 위해 Mock
이라는 가짜 객체를 사용한다.
Mock 객체
의 특정 메소드가 몇 번 호출되었는지, 어떤 값을 전달받았는지, 전달받은 데이터의 형식은 내가 생각한 것이 맞는지 등 다양한 조건을 검사할 수 있다.
자주 사용하는 Mock expect 문법
.toHaveBeenCalledTimes(number)
.toHaveBeenCalledWith(arg1, arg2, ...)
의존성 주입이란 객체 사이의 의존 관계를 외부에 제공하는 방법.
"type": "module",
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest"
},
강의에 나온 스크립트 설정을 했더니
PS D:\jest-test> yarn test
yarn run v1.22.22
$ node --experimental-vm-modules node_modules/.bin/jest
D:\jest-test\node_modules\.bin\jest:2
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
^^^^^^^
SyntaxError: missing ) after argument list
at wrapSafe (node:internal/modules/cjs/loader:1378:20)
at Module._compile (node:internal/modules/cjs/loader:1428:41)
at Module._extensions..js (node:internal/modules/cjs/loader:1548:10)
at Module.load (node:internal/modules/cjs/loader:1288:32)
at Module._load (node:internal/modules/cjs/loader:1104:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)
at node:internal/main/run_main_module:28:49
Node.js v20.17.0
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
이런 오류가 떴다. 대충 Unix 계열의 명령어를 사용해서 window 환경에서는 이 명령어가 작동하지 않기 때문에 오류가 발생한다는 뜻이다.
그래서 윈도우 환경에서는
"type": "module",
"scripts": {
"test": "jest"
},
이렇게 사용해야한다는 내용을 찾았다.
하지만 왜인지 저렇게 사용하니 jest가 ES6의 import는 문법을 해석할 수 없는 문제가 생겼다.
yarn run v1.22.22
$ jest
FAIL ./validation.spec.js
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
D:\jest-test\validation.spec.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { isEmail } from "./validation";
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1505:14)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.291 s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
헤멘 끝에 한 사이트의 글을 발견했는데
출처: https://poiemaweb.com/jest-esm
yarn add -D jest @types/jest @babel/core @babel/preset-env
ts/jest와 babel을 설치해야하고
babel은 ES6문법을 ES6 이전 문법으로 변환시켜주는 역할을 한다.
babel.config.json을 생성해서
{
"presets": ["@babel/preset-env"]
}
을 붙여넣고
jest.config.js를 생성하고
export default {
transform: {
"^.+\\.js$": "babel-jest",
},
testEnvironment: "node",
};
를 붙여넣는다.
이후 테스트
PASS ./validation.spec.js
√ 입력한 이메일 주소에는 @ 문자가 1개만 있어야 이메일 형식이다. (3 ms)
√ 입력한 이메일 주소에 공백(이 존재하면 이메일 형식이 아니다. (1 ms)
√ 입력한 이메일 주소 맨 앞에 하이픈(-)이 있으면 이메일 형식이 아니다. (1 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 0.637 s, estimated 1 s
Ran all test suites.
정상적으로 작동하는 모습.