2/16/2024

MongoDB와 E2E 테스트

발단

E2E 테스트를 작성하고 실행했는데 이상하게 연결 오류가 발생했다. 연결이 되고난 후 데이터베이스 정리 코드를 찾은 다음 다시 테스트를 했는데 다른 오류가 등장했다. Mongoose는 TypeORM과 다른 점이 많아서 이에 대해 적어본다!


데이터베이스 연결

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
describe('Authentication E2E Test', () => {
  let app: INestApplication;
  let createUserDto: CreateUserDto;
  let credentials: {
    email: string;
    password: string;
  };
 
  beforeEach(async () => {
    createUserDto = {
      name'박선심',
      password: '1234Aa!@',
      email: 'sunshim@naver.com',
      role: Role.USER,
    };
 
    credentials = {
      email: 'sunshim@naver.com',
      password: '1234Aa!@',
    };
 
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();
 
    app = moduleFixture.createNestApplication();
    await app.init();
  });
 
  describe('signUp', () => {
    it('should throw BadRequestException for an invalid name.', async () => {
      createUserDto.name = '박선';
 
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.BAD_REQUEST);
    });
 
    it('should throw BadRequestException for an invalid password.', async () => {
      createUserDto.password = '1234';
 
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.BAD_REQUEST);
    });
 
    it('should throw BadRequestException for an invalid email.', async () => {
      createUserDto.email = 'sunshim';
 
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.BAD_REQUEST);
    });
 
    it('should create a user.', async () => {
      const response = await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.CREATED);
 
      const body = response.body;
 
      expect(body.password).not.toBe(createUserDto.password);
      expect(body.email).toBe(createUserDto.email);
    });
 
    it('should throw ConflictException for a duplicate email.', async () => {
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.CREATED);
 
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.CONFLICT);
    });
  });
 
  describe('signIn', () => {
    it('should throw BadRequestException for an invalid email.', async () => {
      credentials.email = 'sfaslkf@naver.com';
 
      await request(app.getHttpServer())
        .post('/auth/signin')
        .send(credentials)
        .expect(HttpStatus.BAD_REQUEST);
    });
 
    it('should throw BadRequestException for an invalid password.', async () => {
      credentials.password = '24391!@svz';
 
      await request(app.getHttpServer())
        .post('/auth/signin')
        .send(credentials)
        .expect(HttpStatus.BAD_REQUEST);
    });
 
    it('should sign in a user.', async () => {
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.CREATED);
 
      const response = await request(app.getHttpServer())
        .post('/auth/signin')
        .send(credentials)
        .expect(HttpStatus.OK);
 
      const headers = response.headers;
      expect(headers).toBeDefined();
 
      const cookies = headers['set-cookie'].map(
        (cookie: string) => cookie.split(';')[0],
      );
 
      const accessToken = cookies
        .find((cookie: string) => cookie.includes('access_token'))
        .split('=')[1];
      const refreshToken = cookies
        .find((cookie: string) => cookie.includes('refresh_token'))
        .split('=')[1];
 
      const body = response.body;
 
      expect(accessToken).toBeDefined();
      expect(refreshToken).toBeDefined();
      expect(body.email).toBe(credentials.email);
    });
  });
 
  describe('signOut', () => {
    it('should sign out a user.', async () => {
      await request(app.getHttpServer())
        .post('/auth/signup')
        .send(createUserDto)
        .expect(HttpStatus.CREATED);
 
      let response = await request(app.getHttpServer())
        .post('/auth/signin')
        .send(credentials)
        .expect(HttpStatus.OK);
 
      const headers = response.headers;
      expect(headers).toBeDefined();
 
      let cookies = headers['set-cookie'];
 
      response = await request(app.getHttpServer())
        .post('/auth/signout')
        .set('Cookie', cookies)
        .expect(HttpStatus.NO_CONTENT);
 
      cookies = response.headers['set-cookie'].map(
        (cookie: string) => cookie.split(';')[0],
      );
 
      const accessToken = cookies
        .find((cookie: string) => cookie.includes('access_token'))
        .split('=')[1];
      const refreshToken = cookies
        .find((cookie: string) => cookie.includes('refresh_token'))
        .split('=')[1];
 
      expect(accessToken).toBe('');
      expect(refreshToken).toBe('');
    });
  });
 
  afterEach(async () => {
    await mongoose.connection.dropCollection('users');
 
    await app.close();
  });
});
cs
코드를 보면 Mongoose 패키지 자체를 가져온 다음 각 테스트 종료 후 사용자 컬렉션을 비우는  dropCollection() 메서드를 호출한다. 하지만 [ExceptionHandler] Unable to connect to the database. Retrying (1)... 오류가 발생했다! 

TypeORM에서 데이터베이스와 상호작용하기 위해 app.get() 메서드로 TypeORM 패키지의 DataSource를 가져온 것처럼 Mongoose 패키지에서 비슷한 메서드를 찾아야 했다. 검색을 통해 알아보니 mongoose.connect() 메서드가 MongoDB와 기본 연결을 연다고 하며 URI를 인자로 가진다.
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
// common.factory.ts
export const buildDatabaseUri = () => {
  return `mongodb://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`;
};
 
// auth.e2e-spec.ts
describe('Authentication E2E Test', () => {
  let app: INestApplication;
  ...
 
  beforeEach(async () => {
    ...
 
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();
 
    app = moduleFixture.createNestApplication();
    await app.init();
  });
 
  ...
 
  afterEach(async () => {
    // 테스트 환경 데이터베이스 URI를 가져온다.
    mongoose.connect(buildDatabaseUri());
 
    await mongoose.connection.dropCollection('users');
 
    await app.close();
  });
});
cs

테스트를 다시 실행했는데 연결 오류는 사라졌는데 MongoError: ns not found 오류가 나왔다... 이건 뭐지?


데이터베이스 정리

데이터베이스는 정리가 되는데 왜 이런 오류가 발생했는지 알아보니 컬렉션이 없어서 발생하는 오류라고~ 따라서 해결책은 자연스럽게 컬렉션을 생성하면 된다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe('Authentication E2E Test', () => {
  ... 
 
  afterEach(async () => {
    mongoose.connect(buildDatabaseUri());
    
    // 컬렉션의 모든 도큐먼트를 제거한다.
    await mongoose.connection.dropCollection('users');
    // 컬렉션을 생성한다.
    await mongoose.connection.createCollection('users');
 
    await mongoose.disconnect();
    await app.close();
  });
});
cs

테스트가 성공적으로 통과된다!

update: 2024.02.16

댓글 없음:

댓글 쓰기