예제 : User 모듈의 end-to-end
test/user.e2e-spec.ts
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
describe("UserModule (e2e)", () => {
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
// 기본적으로 전체 모듈인 AppModule을 import 하기
imports: [AppModule],
}).compile();
app = module.createNestApplication();
await app.init();
});
});
- 기본적으로 전체 모듈인 AppModule을 import 하기
- 앱 모듈안에 모든 모듈이 작성되어있다.
- 전체 애플리케이션을 로드해서 Resolver를 테스트 할 수 있기 때문에 앱모듈 임포트해주기
실행 명령어
npm run test:e2e
// 결과 : 경로 변경 필요
Cannot find module 'src/common/common.constants' from '../src/jwt/jwt.service.ts'
E2E 설정 구성, 경로 변경하기
- E2E 설정을 구성하는 곳은 test/jest-e2e.json 에 위치해있다.
E2E 경로 변경 : moduleNameMapper를 복사
- package.json에 있는 moduleNameMapper를 복사해서 jest-e2e.json에 붙여넣기
- package.json 내부에 있는 jest configuration은 unit test를 위한 jest환경 설정이다.
test/jest-e2e.json
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\\\.(t|j)s$": "ts-jest"
},
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/../src/$1"
}
}
- src로 시작하는 모든 경로를 rootDir로 시작하는 경로 가기
- "^src/(.*)$": "<rootDir>/$1"
- 내부로 더 들어갈 곳이 없다.
- test 밖으로 나가야 한다.
- test폴더의 rootDir을 보면 "rootDir": ".", 이기 때문이다.
- "^src/(.*)$": "<rootDir>/../src/$1"
실행 명령어
npm run test:e2e
// 결과 : Joi 에러, environment 파일이 load되지 않음
FAIL test/user.e2e-spec.ts
● Test suite failed to run
// 반드시 NODE_ENV는 반드시 dev나 prod 중 하나여야 한다. -> test도 추가하기
Config validation error: "NODE_ENV" must be one of [dev, prod].
// environment 파일이 load되지 않았기 때문에 아래 에러가 나온 것
"DB_HOST" is required. "DB_PORT" is required.
"DB_USERNAME" is required.
"DB_PASSWORD" is required.
"DB_DATABASE" is required.
"PRIVATE_KEY" is required.
"MAILGUN_API_KEY" is required.
"MAILGUN_DOMAIN_NAME" is required.
"MAILGUN_FROM_EMAIL" is required.
"MAILGUN_TO_EMAIL" is required
20 | @Module({
21 | imports: [
> 22 | ConfigModule.forRoot({
| ^
23 | isGlobal: true, // ConfigModule이 전역으로 설정되어야 하는지 여부
24 | envFilePath: process.env.NODE_ENV === "dev" ? ".env.dev" : ".env.test",
25 | ignoreEnvFile: process.env.NODE_ENV === "prod", // 서버에 deploy 할 때 환경변수 파일을 사용 하지 않기
at Function.forRoot (../node_modules/@nestjs/config/dist/config.module.js:86:23)
at Object.<anonymous> (../src/app.module.ts:22:18)
at Object.<anonymous> (user.e2e-spec.ts:4:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 14.636 s
에러내용 : Config validation error: "NODE_ENV" must be one of [dev, prod].
반드시 NODE_ENV는 반드시 dev나 prod 중 하나여야 한다.
해결 : test도 추가하기
Joi schema를 수정
app.module.ts
...
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // ConfigModule이 전역으로 설정되어야 하는지 여부
envFilePath: process.env.NODE_ENV === "dev" ? ".env.dev" : ".dev.test",
ignoreEnvFile: process.env.NODE_ENV === "prod", // 서버에 deploy 할 때 환경변수 파일을 사용하지 않기
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid("dev", "prod", "test").required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.string().required(),
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_DATABASE: Joi.string().required(),
PRIVATE_KEY: Joi.string().required(),
MAILGUN_API_KEY: Joi.string().required(),
MAILGUN_DOMAIN_NAME: Joi.string().required(),
MAILGUN_FROM_EMAIL: Joi.string().required(),
MAILGUN_TO_EMAIL: Joi.string().required(),
}),
}),
...
],
})
- NODE_ENV: Joi.string().valid("dev", "prod", "test").required(),
.env.test 파일 내용 추가하기
- package.json파일에서 configModule에 envFilePath를 보면 2가지의 옵션이 있다.
- nvFilePath: process.env.NODE_ENV === "dev" ? ".env.dev" : ".env.test",
- 개발모드가 dev가 아니면 .env.test로 설정되지만 .env.test 파일은 빈 파일로 되어있기 때문에 오류가 났던것!
.env.dev 내용 복사해서 넣기
.env.test
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=woo
DB_PASSWORD=0000
DB_DATABASE=basic-test
PRIVATE_KEY=랜덤키넣기
데이터 베이스 이름은 다르게 해주기
- end-to-end 테스트 하기위해서 데이트베이스 이름은 다르게 해주자
- 데이터베이스에 실제로 relactionship을 만들고, user를 생성해서 테스트 해보기 위해서이다.
- 메일건 API를 다시 발급받기 싫으면 그대로 사용해도 되고 테스트용을 따로 발급받아서 사용해도 된다.
- PRIVATE_KEY 랜덤키 넣기
- 랜덤 키 생성 사이트
- 256-bit key requirement.라 적혀있는 부분에 랜덤 키 복사하기
- 이 방법은 쿠키던가 다른 암호화가 필요한 경우에도 활용 가능하다.
TODO 만들기
test/user.e2e-spec.ts
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
describe("UserModule (e2e)", () => {
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
// 기본적으로 전체 모듈인 AppModule을 import 하기
imports: [AppModule],
}).compile();
app = module.createNestApplication();
await app.init();
});
it.todo("createAccount");
it.todo("login");
it.todo("userProfile");
it.todo("me");
it.todo("editProfile");
it.todo("verifyEmail");
});
실행 명령어
npm run test:e2e
// 결과
PASS test/user.e2e-spec.ts (7.952 s)
UserModule (e2e)
✎ todo me
✎ todo userProfile
✎ todo editProfile
✎ todo createAccount
✎ todo login
✎ todo verifyEmail
beforEach 대신 beforeAll로 변경해서 테스트해보기
test/user.e2e-spec.ts
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
describe("UserModule (e2e)", () => {
let app: INestApplication;
**beforeAll**(async () => {
const module: TestingModule = await Test.createTestingModule({
// 기본적으로 전체 모듈인 AppModule을 import 하기
imports: [AppModule],
}).compile();
app = module.createNestApplication();
await app.init();
});
it.todo("createAccount");
it.todo("login");
it.todo("userProfile");
it.todo("me");
it.todo("editProfile");
it.todo("verifyEmail");
});
- 더 많은 오류를 보기위해 beforEach 대신 beforeAll로 바꿔서 테스트해보기
- 각각의 test 전에 module을 로드하지 않고, 모든 test 전에 module을 로드하고 하기위해 변경
실행 명령어
npm run test:e2e
// 결과 : 데이터베이스 연결 거절
[Nest] 127328 - 2024. 07. 12. 오전 1:31:05
ERROR [TypeOrmModule] Unable to connect to the database.
Retrying (9)...
TypeError: this.postgres.Pool is not a constructor
at PostgresDriver.createPool (C:\\wam\\nuber-eats-backend\\src\\driver\\postgres\\PostgresDriver.ts:1507:22)
at PostgresDriver.connect (C:\\wam\\nuber-eats-backend\\src\\driver\\postgres\\PostgresDriver.ts:357:38)
at DataSource.initialize (C:\\wam\\nuber-eats-backend\\src\\data-source\\DataSource.ts:253:27)
at C:\\wam\\nuber-eats-backend\\node_modules\\@nestjs\\typeorm\\dist\\typeorm-core.module.js:187:30
- 에러 내용 : ERROR [TypeOrmModule] Unable to connect to the database.
- nuber-eats-test라는 데이터베이스를 생성해줘야한다.
실제 테스트 데이터베이스 생성하기
E2E 테스트 하는 방법은 여러가지가 있다.
- mock(가짜) 데이터베이스를 만들어 사용하기
- 실제 데이터베이스를 사용하기
pgAdmin4
실행 명령어
npm run test:e2e
// 결과 : 테스트 현황에 경고 내용
PASS test/user.e2e-spec.ts (8.146 s)
UserModule (e2e)
✎ todo me
✎ todo userProfile
✎ todo editProfile
✎ todo createAccount
✎ todo login
✎ todo verifyEmail
Test Suites: 1 passed, 1 total
Tests: 6 todo, 6 total
Snapshots: 0 total
Time: 8.399 s, estimated 9 s
Ran all test suites.
Jest did not exit one second after the test run has completed.
'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
- 에러내용 :
Jest did not exit one second after the test run has completed.
'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue. - 뭔가가 종료되지 않은 상태에서 jest가 종료되었기 때문에 뜬는 경고이다.
- application을 종료하는 코드를 넣어줘야 한다.
application을 종료하는 코드를 넣기
test/user.e2e-spec.ts
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { after } from "node:test";
describe("UserModule (e2e)", () => {
let app: INestApplication;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
// 기본적으로 전체 모듈인 AppModule을 import 하기
imports: [AppModule],
}).compile();
app = module.createNestApplication();
await app.init();
});
afterAll(async () => {
app.close();
});
it.todo("createAccount");
it.todo("login");
it.todo("userProfile");
it.todo("me");
it.todo("editProfile");
it.todo("verifyEmail");
});
- test가 돌아간 후에 application이 종료된다.
실행 명령어
npm run test:e2e
- 결과는 성공!
테스트할 때 log는 보이지 않는 것으로 설정하기
app.module.ts
...
TypeOrmModule.forRoot({
type: "postgres",
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: process.env.NODE_ENV !== "prod",
// 테스트일 때 로깅 안보기
logging:
process.env.NODE_ENV !== "prod" && process.env.NODE_ENV !== "test",
entities: [User, Verification],
}),
'Frontend > E2E' 카테고리의 다른 글
Resolver 테스트 사용법 예제 (0) | 2024.07.15 |
---|---|
[초기설정] 테스트가 끝난 후 데이터베이스 내용 drop하기 (0) | 2024.07.15 |
[초기설정] E2E 테스트를 설정하는 예제 코드 설명 (0) | 2024.07.15 |
E2E (End-to-End) (0) | 2024.07.15 |