Testing
Dotenv
터미널에서 npm install @nestjs/config
dotenv 는 .env 파일과 일반적인 환경 변수파일을 참조하여 개체를 생성(겹칠시 환경변수 파일 우선)한다.
.env 파일은 배포에 포함되선 안된다. (git 에선 gitignore로 commit되지 않도록 해야)
우리는 개발용(배포용) .env 파일과 테스트용 .env 파일 두 개를 만들것이다.
root 디렉토리에 .env.tset 와 .env.development 파일 두개를 만들자.
1
2
// .env.development
DB_NAME=db.sqlite
1
2
// .env.test
DB_NAME=test.sqlite
AppModule에 아래와 같이 설정해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// app.module.ts
import { Module, ValidationPipe, MiddlewareConsumer } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { ReportsModule } from './reports/reports.module';
import { User } from './users/user.entity';
import { Report } from './reports/reports.entity';
const cookieSession = require('cookie-session');
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`,
}),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
type: 'sqlite',
database: config.get<string>('DB_NAME'),
synchronize: true,
entities: [User, Report],
}
}
}),
// TypeOrmModule.forRoot({
// type: 'sqlite',
// database: 'db.sqlite',
// entities: [User, Report],
// synchronize: true,
// }),
UsersModule,
ReportsModule,
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_PIPE,
useValue: new ValidationPipe({
whitelist: true,
}),
},
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
cookieSession({
keys: ['anything'],
}),
)
.forRoutes('*');
}
}
이제 서버 실행에 마다 알맞은 환경변수를 사용할 수 있도록 적용해주자
npm install cross-env
설치 후 package.json을 수정한다.
1
2
3
4
5
6
7
8
9
10
11
// package.json
"scripts": {
"start": "cross-env NODE_ENV=development nest start", // 수정된 코드
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"test": "cross-env NODE_ENV=test jest",
"test:watch": "cross-env NODE_ENV=test jest --watch --maxWorkers=1",
"test:cov": "cross-env NODE_ENV=test jest --coverage",
"test:debug": "cross-env NODE_ENV=test node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json"
위 코드들에만 앞에 cross-env NODE_ENV= 를 추가한다. 위는 개발, 배포 떄니까 development로 설정하고 아래는 테스트용이니 test로 설정한다.
이제 터미널 2개를 켜서 각각 npm run start:dev
와 npm run test:e2e
를 실행시켜보고, 중복된 아이디로 생성을 반복해서 생성이 거부되는지 확인해보자. 에러가 나야 제대로 작동한다는 뜻이다. 겨로가에 database is locked라고 뜰 텐데, 이제 이 부분을 해결할 것이다.
잠긴 데이터베이스 열기
우리 test 폴더내에는 jest가 app , auth 2가지 인스턴스가 생성되도록 구성되어있다. 각각의 인스턴스가 동일한 SQLite 데이터베이스에 동시에 연결을 시도할텐데, SQLite는 다수의 병렬 연결을 거부한다. 이를 수정하여 하나의 테스트만 실행하도록 하자.
package.json 파일에 가서
"test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json --maxWorkers=1"}
이 부분만 변경해주고 db.sqlite 파일을 삭제한 이후에 npm run test:e2e 를 실행해보면 더이상 dtabase가 잠겼다는 오류가 발생하지 않는다.
테스트 전 DB 정리하기
단일 테스트 전에 데이터를 삭제해야 제대로 테스트를 진행할 수 있다.
DB 내 모든 테이블의 행을 삭제하거나, 전체 DB를 삭제한 후 자동으로 DB를 다시 만들거나. 후자의 방법을 공부해보자.
쉽게 말해 test.sqlite를 삭제하도록 하면 되는것이다.
test 폴더의 jest 파일을 수정하자
1
2
3
4
5
6
7
8
9
10
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"setupFilesAfterEnv": ["<rootDir>/setup.ts"] // 추가한 코드
}
테스트 할 때마다 setup.ts를 실행하게 된다. test디렉토리에 저 파일을 만들자
1
2
3
4
5
6
7
8
9
// setup.ts
import { rm } from 'fs/promises';
import { join } from 'path';
global.beforeEach(async () => {
try {
await rm(join(__dirname, '..', 'test.sqlite'));
} catch (err) {}
});
이제 npm run test:e2e
를 2번 실행해보자. 매번 DB가 삭제되므로 두번 해도 통과해야한다.
테스트 추가
다시 돌아와서, 인증(authentication) 관련 테스트 코드를 추가하자. 테스트 폴더로 간다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// auth.e2e-spec.ts
it('signup as a new user then get the currently logged in user', async () => {
const email = 'asdf@asdf.com';
const res = await request(app.getHttpServer())
.post('/auth/signup')
.send({ email, apssword: 'asdf' })
.expect(201);
const cookie = res.get('Set-Cookie');
const { body } = await request(app.getHttpServer())
.get('/auth/whoami')
.set('Cookie', cookie) //
.expect(200);
expect(body.email).toEqual(email);
});
});
테스트를 통과하는지 확인해보자.