Unit Test (by ChatGPT)
์ ๋ ํ
์คํธ(Unit test)๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ์ฌ์ฉ๋๋ ํ
์คํธ์ ํ ์ข
๋ฅ๋ก, ๊ฐ๋ณ์ ์ธ ์์ค ์ฝ๋์ ์ต์ ๋จ์์ธ ์ ๋(์ผ๋ฐ์ ์ผ๋ก ํจ์, ๋ฉ์๋, ํด๋์ค ๋ฑ)์ด ์๋ํ๋๋ก ๋์ํ๋์ง ๊ฒ์ฆํ๋ ํ
์คํธ์
๋๋ค. ์ ๋ ํ
์คํธ๋ ์์ค ์ฝ๋์ ํน์ ๋ถ๋ถ์ ๊ฒฉ๋ฆฌ์์ผ ํ
์คํธํ๋ฏ๋ก, ๋ค์ํ ์
๋ ฅ๊ฐ๊ณผ ์ํฉ์ ์๋ฎฌ๋ ์ด์
ํ์ฌ ์ฝ๋์ ์ ํ์ฑ, ์์ธ ์ฒ๋ฆฌ, ๊ฒฝ๊ณ ์กฐ๊ฑด ๋ฑ์ ํ์ธํ ์ ์์ต๋๋ค.
์ ๋ ํ
์คํธ์ ๋ชฉ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
์ฝ๋์ ์ ํ์ฑ ๊ฒ์ฆ
: ์ ๋ ํ
์คํธ๋ฅผ ํตํด ์ฝ๋๊ฐ ์๋ํ๋๋ก ๋์ํ๋์ง ํ์ธํ์ฌ ๋ฒ๊ทธ์ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์์ ํ ์ ์์ต๋๋ค.์ฝ๋์ ์์ ์ฑ ํฅ์
: ์ ๋ ํ
์คํธ๋ฅผ ํตํด ์ฝ๋์ ์์ธ ์ฒ๋ฆฌ, ๊ฒฝ๊ณ ์กฐ๊ฑด ๋ฑ์ ํ์ธํ์ฌ ์ฝ๋์ ์์ ์ฑ์ ํฅ์์ํฌ ์ ์์ต๋๋ค.๋ฆฌํฉํ ๋ง ์ง์
: ์ ๋ ํ
์คํธ๋ ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ ๋ ๊ธฐ์กด ๊ธฐ๋ฅ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋์ง ํ์ธํ๋ ๋ฐ ๋์์ ์ค๋๋ค.
๊ฐ๋ฐ ์๋ ํฅ์: ์ ๋ ํ
์คํธ๋ฅผ ํตํด ๋ฒ๊ทธ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ์์ ํ๋ฏ๋ก, ์์ฐ์ฑ์ ํฅ์์ํฌ ์ ์์ต๋๋ค.ํ์
๊ณผ ์ ์ง๋ณด์ ์ฉ์ด์ฑ ํฅ์
: ์ ๋ ํ
์คํธ๋ ์ฝ๋์ ๋์์ ๋ช
ํํ๊ฒ ๋ฌธ์ํํ๊ณ , ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์ดํดํ๊ณ ์ ์ง๋ณด์ํ๋ ๋ฐ ๋์์ ์ค๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ข์ ์ ๋ ํ
์คํธ๋ ๋
๋ฆฝ์ ์ผ๋ก ์คํ ๊ฐ๋ฅํ๋ฉฐ, ๋น ๋ฅด๊ฒ ์คํ๋์ด์ผ ํ๋ฉฐ, ์ ํํ๊ณ ๋ช
ํํ ๊ฒ์ฆ ๋ก์ง์ ๊ฐ์ ธ์ผ ํฉ๋๋ค. ์ ๋ ํ
์คํธ๋ ๊ฐ๋ฐ์๊ฐ ์์ฑํ๋ฉฐ, ์ํํธ์จ์ด ๊ฐ๋ฐ์ ํ์ง๊ณผ ์์ ์ฑ์ ๋์ด๋ ์ค์ํ ๋๊ตฌ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
Example
๋ณดํต ๋๋ถ๋ถ์ ์๋น์ค์ ์กด์ฌํ๋, Authentication/ User service ๊ฐ ๋ด๊ฐ ์๋ํ๋๋๋ก ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํ unit test๋ฅผ ์์ฑํ๋ ค๊ณ ํ๋ค.
์ค์ ๊ฐ์ฒด๋ฅผ ์ ์ธํ๋ฉด์ ์ ๋ํ
์คํธ ์์ฑ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| describe('The AuthenticationService', () => {
let authenticationService : AuthenticationService;
beforeEach(()=>{
authenticationService = new AuthenticationService(
new UsersService(
new Repository<User>()
),
new JwtService({
secretOrPrivateKey:"key"
}),
new ConfigService(),
);
})
describe('when creating a cookie', () => {
it('should return a string', () => {
const userId = 1;
expect(
typeof authenticationService.getCookieWithJwtToken(userId)
).toEqual('string')
})
})
});
|
์์ ๊ฐ์ด ์ฝ๋๋ฅผ ๊ตฌ์ฑํ ๊ฒฝ์ฐ, Authentication Service์ ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋๋ง๋ค ํ์ํ dependency๋ฅผ ์๋์ผ๋ก ๋ฃ์ด์ค์ผ ํ๋ฏ๋ก, ์ฌ๋ฐ๋ฅธ ์์ฑ๋ฐฉ๋ฒ์ ์๋๋ค.
dependency๋ฅผ ์๋์ผ๋ก ๋ฃ์ด์ผ ํ๋ ์ด์ ํด๊ฒฐ
๊ทธ๋ฌ๋ฏ๋ก, ์ฐ๋ฆฌ๋ @nestjs/testing
๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Test.createTestingModule().compile()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด dependency๋ฅผ ์๋์ผ๋ก ๋ฃ์ด์ค์ผ ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
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
| describe('The AuthenticationService', () => {
let authenticationService : AuthenticationService;
beforeEach(async ()=>{
const module = await Test.createTestingModule({
imports: [
UsersModule,
ConfigModule.forRoot({
validationSchema: Joi.object({
POSTGRES_HOST: Joi.string().required(),
POSTGRES_PORT: Joi.number().required(),
POSTGRES_USER: Joi.string().required(),
POSTGRES_PASSWORD: Joi.string().required(),
POSTGRES_DB: Joi.string().required(),
JWT_SECRET: Joi.string().required(),
JWT_EXPIRATION_TIME: Joi.string().required(),
PORT: Joi.number(),
})
}),
DatabaseModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: {
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
},
}),
}),
],
providers: [
AuthenticationService
],
}).compile();
authenticationService = await module.get<AuthenticationService>(AuthenticationService);
})
describe('when creating a cookie', () => {
it('should return a string', () => {
const userId = 1;
expect(
typeof authenticationService.getCookieWithJwtToken(userId)
).toEqual('string')
})
})
});
|
์ค์ DB๋ฅผ ์ฌ์ฉํ์ง ์๊ณ DB mock ๊ฐ์ฒด๋ฅผ ์์ฑ
์์ ๊ฐ์ด Test Module ํ์ํ module๋ค์ ๋ฏธ๋ฆฌ importํด์ dependency๋ฅผ ๋งค๋ฒ ์๋์ผ๋ก ๋ฃ์ด์ค์ผ ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ๋ ์์ง๋ง, ํ์ฌ import๋๋ ๋ชจ๋ ์ค Database Module์ ์ค์ DB๋ฅผ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ ํ
์คํธ์ ์ ํฉํ์ง ์๋ค.
๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ DB Module์ mocking
ํด์ provider๋ก ์ ๊ณตํด์ค์ผ ํ๋ค.
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
| describe('The AuthenticationService', () => {
let authenticationService : AuthenticationService;
beforeEach(async ()=>{
const module = await Test.createTestingModule({
imports: [
UsersModule,
ConfigModule.forRoot({
validationSchema: Joi.object({
POSTGRES_HOST: Joi.string().required(),
POSTGRES_PORT: Joi.number().required(),
POSTGRES_USER: Joi.string().required(),
POSTGRES_PASSWORD: Joi.string().required(),
POSTGRES_DB: Joi.string().required(),
JWT_SECRET: Joi.string().required(),
JWT_EXPIRATION_TIME: Joi.string().required(),
PORT: Joi.number(),
})
}),
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: {
expiresIn: `${configService.get('JWT_EXPIRATION_TIME')}s`,
},
}),
}),
],
providers: [
UsersService,
AuthenticationService,
{
provide:getRepositoryToken(User),
useValue:{}
}
],
}).compile();
authenticationService = await module.get<AuthenticationService>(AuthenticationService);
})
describe('when creating a cookie', () => {
it('should return a string', () => {
const userId = 1;
expect(
typeof authenticationService.getCookieWithJwtToken(userId)
).toEqual('string')
})
})
});
|
์์ ๊ฐ์ด mocked User Repository๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด
1
2
3
4
| {
provide:getRepositoryToken(User),
useValue:{}
}
|
ํด๋น ๊ฐ์ฒด๋ฅผ provider์ ์ ๊ณตํด์คฌ๋ค. ์ถ๊ฐ๋ก, UserRepository๋ User Module์์ TypeormModule.forFeature([User])
๋ก import ๋๊ณ ์๊ธฐ ๋๋ฌธ์, UserService๋ provider๋ก ์ ๊ณตํ ๊ฒ์ ๋ณผ ์ ์๋ค.
ConfigService / JwtService์ ๋ํ mock ๊ฐ์ฒด ์์ฑ
์ฌ๊ธฐ์ ๋ ๋์๊ฐ์, ConfigModule
๊ณผ JwtModule์
์ง์ importํ๋๊ฒ ์๋, ๊ฐ๊ฐ์ mock ๊ฐ์ฒด๋ก ์์ฑํด์ ์ด๋ฅผ provider๋ก ์ ๊ณตํ๋ ์์
์ ์ฒ๋ฆฌํ๊ฒ ๋ค.
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
| const mockedConfigService = {
get(key: string) {
switch (key) {
case 'JWT_EXPIRATION_TIME':
return '3600'
}
}
}
const mockedJwtService = {
sign: () => ''
}
describe('The AuthenticationService', () => {
let authenticationService : AuthenticationService;
beforeEach(async ()=>{
const module = await Test.createTestingModule({
providers: [
UsersService,
AuthenticationService,
{
provide:getRepositoryToken(User),
useValue:{}
},
{
provide:ConfigService,
useValue: mockedConfigService
},
{
provide:JwtService,
useValue:mockedJwtService
}
],
}).compile();
authenticationService = await module.get<AuthenticationService>(AuthenticationService);
})
describe('when creating a cookie', () => {
it('should return a string', () => {
const userId = 1;
expect(
typeof authenticationService.getCookieWithJwtToken(userId)
).toEqual('string')
})
})
});
|
Jest.fn() ์ ์ด์ฉํด mock ๊ฐ์ฒด ์์ฑ
User Service๋ jest.fn()
์ ์ด์ฉํด mock ๊ฐ์ฒด๋ฅผ ์์ฑํด์ dependency๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ ํด๊ฒฐํ ์ ์์ต๋๋ค.
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
| describe('The UsersService', () => {
let usersService: UsersService;
let findOne: jest.Mock;
beforeEach(async () => {
findOne = jest.fn();
const module = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: {
findOne
}
}
],
})
.compile();
usersService = await module.get(UsersService);
})
describe('when getting a user by email', () => {
describe('and the user is matched', () => {
let user: User;
beforeEach(() => {
user = new User();
findOne.mockReturnValue(Promise.resolve(user));
})
it('should return the user', async () => {
const fetchedUser = await usersService.getByEmail('test@test.com');
expect(fetchedUser).toEqual(user);
})
})
describe('and the user is not matched', () => {
beforeEach(() => {
findOne.mockReturnValue(undefined);
})
it('should throw an error', async () => {
await expect(usersService.getByEmail('test@test.com')).rejects.toThrow();
})
})
})
});
|