Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NDD-103] Member API E2E 테스트 (1h / 1h) #29

Merged
merged 5 commits into from
Nov 11, 2023
Merged

Conversation

quiet-honey
Copy link
Collaborator

@quiet-honey quiet-honey commented Nov 10, 2023

NDD-103 Powered by Pull Request Badge

Why

회원 정보를 가져오는 Member API에 대한 E2E 테스트를 수행하기 위함

How

Reference와 ChatGPT를 참고하여, Jest를 사용한 E2E 테스트 코드를 작성

  1. Member API에 대한 성공 테스트 코드를 작성
const oauthRequestFixture = {
  email: 'fixture@example.com',
  name: 'fixture',
  img: 'https://test.com',
} as OAuthRequest;

const validToken = (await authService.login(oauthRequestFixture)).replace(
  'Bearer ',
  '',
);
const response = await request(app.getHttpServer())
  .get('/api/member')
  .set('Authorization', `Bearer ${validToken}`)
  .expect(200);

expect(response.body.email).toBe(oauthRequestFixture.email);
expect(response.body.nickname).toBe(oauthRequestFixture.name);
expect(response.body.profileImg).toBe(oauthRequestFixture.img);

유효한 Access Token을 소셜 로그인을 사용하여서 직접 발급 받고 싶었으나, 테스트 코드로 OAuth 로그인을 시도하는 것은 불가능하여 어쩔 수 없이 임의의 MeberID를 사용하여 토큰을 발급받고, 이를 사용하여 200 응답을 받을 수 있도록 테스트 코드 작성

  1. Member API에 대한 실패 테스트 코드를 작성
const invalidToken = 'INVALID_TOKEN';

await request(app.getHttpServer())
.get('/api/member')
.set('Authorization', `Bearer ${invalidToken}`)
.expect(401);

위 코드와 같이 유효하지 않은 Access Token을 가지고 요청을 보냄으로써 401 응답 받도록 테스트 코드 작성

Result

테스트

아래 2가지 경우에 대한 테스트를 진행

  1. 회원 정보를 가져오는 API가 요청에 성공적으로 응답하는 경우
  2. 회원 정보를 가져오는 API가 유효하지 않은 요청의 Access Token으로 인해 401(UnAuthorized) 응답하는 경우
    image

테스팅 모듈을 생성하는 Util 메서드

export const createTestModuleFixture = async (
  imports: unknown,
  controllers: unknown,
  providers: unknown,
) => {
  const moduleFixture: TestingModule = await Test.createTestingModule({
    imports: imports,
    controllers: controllers,
    providers: providers,
  } as ModuleMetadata).compile();

  return moduleFixture;
};

NestApp 생성 시에 사용하는 TestingModuleFixture를 생성하는 Util 메서드를 구현하였습니다.
하지만 이를 적용하면, import에 문제가 발생하여서 테스트 코드에 대한 너무 많은 시간이 소비되기에 추후 적용하기로 결정하였습니다.

Prize

저번 단위 테스트 코드 작성에 이어 이번에는 인수 테스트를 위한 코드를 직접 작성하면서 점점 테스트 코드 작성에 대해 친숙해지고 있다.
Member API가 실제 사용자의 요청 시에 상황에 맞게 의도대로 동작한다는 기술적 확신을 얻을 수 있었다.

Reference

E2E API 테스트를 위한 Ref

1. /api/member GET API 에 대한 성공 테스트
2. /api/member GET API에서 유효하지 않은 토큰 사용으로 인한 실패 테스트
위의 2가지의 경우에 대한 테스트 코드 작성
@quiet-honey quiet-honey added BE 백엔드 코드 변경사항 test 테스트코드가 변경된 경우 labels Nov 10, 2023
@quiet-honey quiet-honey self-assigned this Nov 10, 2023
});

it('GET /api/member (회원 정보 반환 성공)', async () => {
const memberId = 1; // 항상 DB에 들어있는 회원의 ID로의 설정이 필요해보입니다.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분에 대한 고민이 많습니다... 이렇게 진행하면 DB에 영구적으로 1이라는 ID를 가지는 Member를 항상 넣어놔야 하는데.. 이게 말이 되나 싶구요..
소셜 로그인을 코드로 해결할 수가 없다는게 이런 문제를 발생시키는 것 같습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇다면, 테스트시에 DB의 모든 데이터를 지우는 로직을 테스트 전체 후에 넣는 것은 어떨까요???
beforeEach, beforeAll 혹은 afterAll로 해당 문제를 해결할 수 있을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 좋은 생각입니다 고민 해보겠습니다!!!

});

it('GET /api/member (회원 정보 반환 성공)', async () => {
const memberId = 1; // 항상 DB에 들어있는 회원의 ID로의 설정이 필요해보입니다.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇다면, 테스트시에 DB의 모든 데이터를 지우는 로직을 테스트 전체 후에 넣는 것은 어떨까요???
beforeEach, beforeAll 혹은 afterAll로 해당 문제를 해결할 수 있을 것 같습니다!

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule, TokenModule],
}).compile();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분의 반복구조를 빼낼 수 있을 것 같습니다!!!

const createTestApp = async (moduleOptions: 타입은 직접 들어가서 봐야겠군요!) => {
    const moduleFixture: TestingModule = await Test.createTestingModule(moduleOptions).compile();
    return moduleFixture.createNestApplication();
}

이렇게 한 후에, 따로 const로 옵션들을 가져와서 테스트 app의 반복구조를 줄이는건 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋은 생각입니다 한번 시도해보겠습니다!!!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@milk717 milk717 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오늘 팀회고때 나왔던 이야기를 듣고 PR을 구경하러 왔습니다! 제가 Nest는 잘 몰라서 유의미한 조언을 해드리긴 어렵지만 당장 눈에 보이는게 있어서 리뷰 남깁니다!

Comment on lines 2 to 8
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
import { CORS_HEADERS, CORS_ORIGIN } from './cors.secure';

export const CORS_CONFIG: CorsOptions = {
origin: CORS_ORIGIN,
credentials: true,
exposedHeaders: CORS_HEADERS,
} No newline at end of file
origin: CORS_ORIGIN,
credentials: true,
exposedHeaders: CORS_HEADERS,
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

두분에 프리티어 포맷 설정이 다르게 되어있어서 이런 히스토리가 남게 된걸까요??
동일한 프리티어 설정을 사용했다면 이런 커밋은 발생하지 않았을 것 같습니다! 추후에 변경내역이 많아진다면 이런 사소한 포맷 변경사항으로 인해 file changed가 늘어나는 것은 불편할 것이라고 생각해서 시간 될 때 한번 맞춰놓는 것도 좋다고 생각합니다!
혹시 이번 커밋은 특별한 경우고 평소에 이런 문제를 겪지 않으신거라면 제 리뷰는 가볍게 무시해주셔도 됩니다~
이상! 리뷰는 남겨드리고 싶었지만 Nest는 몰라서 프리티어만 참견하고간 프론트입니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 지나치지 않으시고 이런 리뷰까지 남겨주셔서 너무 감사합니다:)
동일한 프리티어 파일을 사용하는데도 이런 문제가 있는지 확인할 필요는 있을 것 같습니다!
오늘 한번 확인해보겠습니다~~~ 감사합니다!!!!!

Copy link

cloudflare-workers-and-pages bot commented Nov 11, 2023

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: e68ac91
Status:🚫  Build failed.

View logs

Copy link
Collaborator

@JangAJang JangAJang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :)

@quiet-honey quiet-honey merged commit bf4c6c5 into dev Nov 11, 2023
1 check failed
@delete-merged-branch delete-merged-branch bot deleted the feature/NDD-103 branch November 11, 2023 09:15
adultlee added a commit that referenced this pull request Nov 11, 2023
* [NDD-103] Member API E2E 테스트 (1h / 1h) (#29)

* test: Member API E2E 테스트 코드 작성

1. /api/member GET API 에 대한 성공 테스트
2. /api/member GET API에서 유효하지 않은 토큰 사용으로 인한 실패 테스트
위의 2가지의 경우에 대한 테스트 코드 작성

* style: lint 적용

* test: 인수 테스트 코드 로직 변경 완료

GET /api/member에 대한 E2E 테스트 완료

* style: Window 개행문자 관련 에러 해결을 위한 lint 설정 추가

* feat: TestingModule을 생성하는 Util 메서드 구현

* [NDD-87] Interview 페이지 camera 컴포넌트 기능 부여 (7h/8h) (#30)

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: InterviewCamera 내부 state 정의

* feat: 현재 브라우저에서 지원가능 한 MimeType을 명시

* feat: 현재 Media에 연결 기능 구현

* feat: 마운트시 바로 stream 연결

* feat: Record 시작함수 구현

* feat: record stop 함수 구현

* feat: Record Download 함수 구현

* feat: video UI 기능 구현

* fix: log 문제

* fix: InterviewCamera 파일시스템 문제 해결

* chore: 상대경로 수정

* chore:  CameraRef 이름 변경

---------

Co-authored-by: quiet-honey <99426344+quiet-honey@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BE 백엔드 코드 변경사항 test 테스트코드가 변경된 경우
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants