Skip to content

Commit 32fe3fe

Browse files
authored
Merge pull request #506 from boostcampwm-2024/perf/test-parallel
⚡️ perf: E2E 테스트 병렬 실행 개선 (3)
2 parents 547ceae + d8c4a4a commit 32fe3fe

File tree

69 files changed

+1361
-1073
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1361
-1073
lines changed

server/src/app.module.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { FeedModule } from './feed/module/feed.module';
1111
import { WinstonLoggerModule } from './common/logger/logger.module';
1212
import { ChatModule } from './chat/module/chat.module';
1313
import { StatisticModule } from './statistic/module/statistic.module';
14-
import { TestModule } from './common/test/test.module';
1514
import { UserModule } from './user/module/user.module';
1615
import { ActivityModule } from './activity/module/activity.module';
1716
import { EmailModule } from './common/email/email.module';
@@ -60,7 +59,6 @@ const exists = !!chosen && fs.existsSync(chosen);
6059
}),
6160
WinstonLoggerModule,
6261
RedisModule,
63-
TestModule,
6462
EmailModule,
6563
MetricsModule,
6664
AdminModule,

server/src/common/database/load.config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ export function loadDBSetting(configService: ConfigService) {
55
const isDev = env === 'LOCAL' || env === 'DEV';
66
const isTest = env === 'TEST';
77

8+
const workerId = process.env.JEST_WORKER_ID;
9+
10+
const database = isTest
11+
? `denamu_test_${workerId}`
12+
: configService.get<string>('DB_NAME');
13+
814
return {
9-
type: configService.get<'mysql' | 'sqlite'>('DB_TYPE'),
10-
database: configService.get<string>('DB_NAME'),
15+
type: configService.get<'mysql'>('DB_TYPE'),
16+
database,
1117
host: configService.get<string>('DB_HOST'),
1218
port: configService.get<number>('DB_PORT'),
1319
username: configService.get<string>('DB_USER'),

server/src/common/redis/redis.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import Redis from 'ioredis';
1111
provide: 'REDIS_CLIENT',
1212
inject: [ConfigService],
1313
useFactory: (configService: ConfigService) => {
14+
const workerId = Number(process.env.JEST_WORKER_ID ?? 0);
15+
1416
return new Redis({
1517
host: configService.get<string>('REDIS_HOST'),
1618
port: configService.get<number>('REDIS_PORT'),
1719
username: configService.get<string>('REDIS_USER'),
1820
password: configService.get<string>('REDIS_PASSWORD'),
21+
db: workerId,
1922
});
2023
},
2124
},

server/src/common/test/test.module.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

server/src/common/test/test.service.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

server/src/user/scheduler/user.scheduler.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ export class UserScheduler {
5050
}
5151
} catch (error) {
5252
this.logger.error(
53-
`[UserScheduler]: streak 업데이트 스케줄러 동작중 오류 발생: ${error.message}`,
54-
error.stack,
53+
`[UserScheduler]: streak 업데이트 스케줄러 동작중 오류 발생: ${error}`,
5554
);
5655
}
5756
}

server/test/activity/e2e/get.e2e-spec.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpStatus, INestApplication } from '@nestjs/common';
1+
import { HttpStatus } from '@nestjs/common';
22
import * as supertest from 'supertest';
33
import { UserRepository } from '../../../src/user/repository/user.repository';
44
import { ActivityRepository } from '../../../src/activity/repository/activity.repository';
@@ -7,30 +7,31 @@ import { ActivityFixture } from '../../config/common/fixture/activity.fixture';
77
import { User } from '../../../src/user/entity/user.entity';
88
import TestAgent from 'supertest/lib/agent';
99
import { ReadActivityQueryRequestDto } from '../../../src/activity/dto/request/readActivity.dto';
10+
import { Activity } from '../../../src/activity/entity/activity.entity';
11+
import { testApp } from '../../config/e2e/env/jest.setup';
1012

1113
const URL = '/api/activity';
1214

1315
describe(`GET ${URL}/{userId} E2E Test`, () => {
14-
let app: INestApplication;
1516
let user: User;
16-
let activities: Array<{ activityDate: Date; viewCount: number }>;
17+
let activities: Activity[];
1718
let agent: TestAgent;
19+
let activityRepository: ActivityRepository;
20+
let userRepository: UserRepository;
1821

19-
beforeAll(async () => {
20-
app = global.testApp;
21-
agent = supertest(app.getHttpServer());
22-
const userRepository = app.get(UserRepository);
23-
const activityRepository = app.get(ActivityRepository);
24-
user = await userRepository.save(UserFixture.createUserFixture());
25-
activities = Array.from({ length: 5 }).map((_, i) =>
26-
ActivityFixture.createActivityFixture(
27-
user,
28-
{ viewCount: (i + 1) * 2 },
29-
i,
30-
),
31-
);
22+
beforeAll(() => {
23+
agent = supertest(testApp.getHttpServer());
24+
activityRepository = testApp.get(ActivityRepository);
25+
userRepository = testApp.get(UserRepository);
26+
});
3227

33-
await activityRepository.insert(activities);
28+
beforeEach(async () => {
29+
user = await userRepository.save(
30+
await UserFixture.createUserCryptFixture(),
31+
);
32+
activities = await activityRepository.save(
33+
ActivityFixture.createActivitiesFixture(user, 3),
34+
);
3435
});
3536

3637
it('[404] 존재하지 않는 사용자 ID로 요청할 경우 활동 데이터 조회를 실패한다.', async () => {
@@ -61,13 +62,12 @@ describe(`GET ${URL}/{userId} E2E Test`, () => {
6162

6263
// Http then
6364
const { data } = response.body;
64-
const expectedDailyActivities = activities.map((activity) => ({
65-
date: activity.activityDate.toISOString().split('T')[0],
66-
viewCount: activity.viewCount,
67-
}));
6865
expect(response.status).toBe(HttpStatus.OK);
6966
expect(data).toStrictEqual({
70-
dailyActivities: expectedDailyActivities,
67+
dailyActivities: activities.map((activity) => ({
68+
date: activity.activityDate.toISOString().split('T')[0],
69+
viewCount: activity.viewCount,
70+
})),
7171
maxStreak: user.maxStreak,
7272
currentStreak: user.currentStreak,
7373
totalViews: user.totalViews,

server/test/admin/e2e/login.e2e-spec.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,46 @@
1-
import { AdminFixture } from './../../config/common/fixture/admin.fixture';
2-
import { HttpStatus, INestApplication } from '@nestjs/common';
1+
import {
2+
ADMIN_DEFAULT_PASSWORD,
3+
AdminFixture,
4+
} from './../../config/common/fixture/admin.fixture';
5+
import { HttpStatus } from '@nestjs/common';
36
import { LoginAdminRequestDto } from '../../../src/admin/dto/request/loginAdmin.dto';
47
import * as supertest from 'supertest';
58
import { AdminRepository } from '../../../src/admin/repository/admin.repository';
69
import TestAgent from 'supertest/lib/agent';
710
import { RedisService } from '../../../src/common/redis/redis.service';
811
import { REDIS_KEYS } from '../../../src/common/redis/redis.constant';
912
import * as uuid from 'uuid';
13+
import { Admin } from '../../../src/admin/entity/admin.entity';
14+
import { testApp } from '../../config/e2e/env/jest.setup';
1015

1116
const URL = '/api/admin/login';
1217

1318
describe(`POST ${URL} E2E Test`, () => {
14-
let app: INestApplication;
1519
let agent: TestAgent;
1620
let redisService: RedisService;
21+
let admin: Admin;
22+
let adminRepository: AdminRepository;
1723
const redisKeyMake = (data: string) => `${REDIS_KEYS.ADMIN_AUTH_KEY}:${data}`;
1824
const sessionKey = 'admin-login-sessionKey';
1925

20-
beforeAll(async () => {
21-
app = global.testApp;
22-
agent = supertest(app.getHttpServer());
23-
redisService = app.get(RedisService);
24-
const adminRepository = app.get(AdminRepository);
25-
await adminRepository.insert(await AdminFixture.createAdminCryptFixture());
26+
beforeAll(() => {
27+
agent = supertest(testApp.getHttpServer());
28+
redisService = testApp.get(RedisService);
29+
adminRepository = testApp.get(AdminRepository);
2630
});
2731

28-
beforeEach(() => {
32+
beforeEach(async () => {
2933
jest.spyOn(uuid, 'v4').mockReturnValue(sessionKey as any);
34+
admin = await adminRepository.save(
35+
await AdminFixture.createAdminCryptFixture(),
36+
);
3037
});
3138

3239
it('[401] 등록되지 않은 ID로 로그인할 경우 로그인을 실패한다.', async () => {
3340
// given
3441
const requestDto = new LoginAdminRequestDto({
3542
loginId: 'testWrongAdminId',
36-
password: AdminFixture.GENERAL_ADMIN.password,
43+
password: ADMIN_DEFAULT_PASSWORD,
3744
});
3845

3946
// Http when
@@ -54,7 +61,7 @@ describe(`POST ${URL} E2E Test`, () => {
5461
it('[401] 비밀번호가 다를 경우 로그인을 실패한다.', async () => {
5562
// given
5663
const requestDto = new LoginAdminRequestDto({
57-
loginId: AdminFixture.GENERAL_ADMIN.loginId,
64+
loginId: admin.loginId,
5865
password: 'testWrongAdminPassword!',
5966
});
6067

@@ -76,8 +83,8 @@ describe(`POST ${URL} E2E Test`, () => {
7683
it('[200] 존재하는 사용자의 정보로 로그인할 경우 로그인을 성공한다.', async () => {
7784
// given
7885
const requestDto = new LoginAdminRequestDto({
79-
loginId: AdminFixture.GENERAL_ADMIN.loginId,
80-
password: AdminFixture.GENERAL_ADMIN.password,
86+
loginId: admin.loginId,
87+
password: ADMIN_DEFAULT_PASSWORD,
8188
});
8289

8390
// Http when
@@ -93,6 +100,6 @@ describe(`POST ${URL} E2E Test`, () => {
93100
const savedSession = await redisService.get(redisKeyMake(sessionKey));
94101

95102
// DB, Redis then
96-
expect(savedSession).toBe(AdminFixture.GENERAL_ADMIN.loginId);
103+
expect(savedSession).toBe(admin.loginId);
97104
});
98105
});

server/test/admin/e2e/logout.e2e-spec.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import { HttpStatus, INestApplication } from '@nestjs/common';
1+
import { HttpStatus } from '@nestjs/common';
22
import * as supertest from 'supertest';
33
import { RedisService } from '../../../src/common/redis/redis.service';
44
import { REDIS_KEYS } from '../../../src/common/redis/redis.constant';
55
import TestAgent from 'supertest/lib/agent';
6+
import { testApp } from '../../config/e2e/env/jest.setup';
67

78
const URL = '/api/admin/logout';
89

910
describe(`POST ${URL} E2E Test`, () => {
10-
let app: INestApplication;
1111
let agent: TestAgent;
1212
let redisService: RedisService;
1313
const redisKeyMake = (data: string) => `${REDIS_KEYS.ADMIN_AUTH_KEY}:${data}`;
1414
const sessionKey = 'admin-logout-sessionKey';
1515
const sessionId = 'test1234';
1616

17-
beforeAll(async () => {
18-
app = global.testApp;
19-
agent = supertest(app.getHttpServer());
20-
redisService = app.get(RedisService);
17+
beforeAll(() => {
18+
agent = supertest(testApp.getHttpServer());
19+
redisService = testApp.get(RedisService);
20+
});
21+
22+
beforeEach(async () => {
2123
await redisService.set(redisKeyMake(sessionKey), sessionId);
2224
});
2325

@@ -70,9 +72,9 @@ describe(`POST ${URL} E2E Test`, () => {
7072
expect(data).toBeUndefined();
7173

7274
// DB, Redis when
73-
const savedSessionId = await redisService.get(redisKeyMake(sessionKey));
75+
const savedSession = await redisService.get(redisKeyMake(sessionKey));
7476

7577
// DB, Redis then
76-
expect(savedSessionId).toBeNull();
78+
expect(savedSession).toBeNull();
7779
});
7880
});

server/test/admin/e2e/register.e2e-spec.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpStatus, INestApplication } from '@nestjs/common';
1+
import { HttpStatus } from '@nestjs/common';
22
import { RegisterAdminRequestDto } from '../../../src/admin/dto/request/registerAdmin.dto';
33
import * as supertest from 'supertest';
44
import { AdminFixture } from '../../config/common/fixture/admin.fixture';
@@ -7,27 +7,25 @@ import TestAgent from 'supertest/lib/agent';
77
import { RedisService } from '../../../src/common/redis/redis.service';
88
import { REDIS_KEYS } from '../../../src/common/redis/redis.constant';
99
import * as bcrypt from 'bcrypt';
10-
import { Admin } from '../../../src/admin/entity/admin.entity';
10+
import { testApp } from '../../config/e2e/env/jest.setup';
1111

1212
const URL = '/api/admin/register';
1313

1414
describe(`POST ${URL} E2E Test`, () => {
15-
let app: INestApplication;
1615
let agent: TestAgent;
1716
let adminRepository: AdminRepository;
18-
let admin: Admin;
17+
let redisService: RedisService;
1918
const sessionKey = 'admin-register-session-key';
2019
const redisKeyMake = (data: string) => `${REDIS_KEYS.ADMIN_AUTH_KEY}:${data}`;
2120

22-
beforeAll(async () => {
23-
app = global.testApp;
24-
agent = supertest(app.getHttpServer());
25-
adminRepository = app.get(AdminRepository);
26-
const redisService = app.get(RedisService);
27-
admin = await adminRepository.save(
28-
await AdminFixture.createAdminCryptFixture(),
29-
);
30-
await redisService.set(redisKeyMake(sessionKey), admin.loginId);
21+
beforeAll(() => {
22+
agent = supertest(testApp.getHttpServer());
23+
adminRepository = testApp.get(AdminRepository);
24+
redisService = testApp.get(RedisService);
25+
});
26+
27+
beforeEach(async () => {
28+
await redisService.set(redisKeyMake(sessionKey), 'testAdminId');
3129
});
3230

3331
it('[401] 관리자 로그인 쿠키가 없을 경우 회원가입을 실패한다.', async () => {
@@ -83,9 +81,12 @@ describe(`POST ${URL} E2E Test`, () => {
8381

8482
it('[409] 중복된 ID로 회원가입을 할 경우 다른 관리자 계정 회원가입을 실패한다.', async () => {
8583
// given
84+
const admin = await adminRepository.save(
85+
await AdminFixture.createAdminCryptFixture(),
86+
);
8687
const newAdminDto = new RegisterAdminRequestDto({
87-
loginId: AdminFixture.GENERAL_ADMIN.loginId,
88-
password: AdminFixture.GENERAL_ADMIN.password,
88+
loginId: admin.loginId,
89+
password: 'testNewAdminPassword!',
8990
});
9091

9192
// Http when
@@ -136,8 +137,5 @@ describe(`POST ${URL} E2E Test`, () => {
136137
expect(
137138
await bcrypt.compare(newAdminDto.password, savedAdmin.password),
138139
).toBeTruthy();
139-
140-
// cleanup
141-
await adminRepository.delete({ loginId: newAdminDto.loginId });
142140
});
143141
});

0 commit comments

Comments
 (0)