-
Notifications
You must be signed in to change notification settings - Fork 1
1114(화) BE 회의록
- 12시부터 페어 프로그래밍
- 테스트 코드 작성
- board, auth CRUD 구현
- 개발 기록 정리
- ncp 서버 생성 과정
- docker 이미지 생성 과정
- TDD 과정
- 9시에 멘토님과 멘토링
이번에 팀 레포 프로젝트 폴더를 완전히 갈아 엎으면서 fork로 떠온 개인 레포에서 다시 upstream(팀 레포)를 fetch, rebase할 일이 생겼다.
사실 fork해온 저장소를 지우고 다시 fork를 했으면 쉽게 세팅이 됐겠지만 git 연습도 해볼 겸 그대로 fetch, rebase를 진행하였다.
- 참고자료(학습메모 1) : [GIT] Conflict(충돌) 났을 때 강제로 Pull 하기.
- fork repo git clone
- remote add upstream
- fetch, reset upstream main
- git log
- git push -f origin main ( fork해온 개인 repo에 push)
- fetch, rebase be-develop (upstream의 be-develop 브랜치 fetch, rebase)
- git log 확인
- rebase be-develop with upstream main
- push origin be-develop
- final git graph
현재 프로젝트 be-develop 브랜치에 대한 local PC로의 pull을 완료한 후 yarn install을 해준다.
yarn install
VSCode의 경우는 아래와 같이 @yarnpkg/sdks, vscode를 dlx로 세팅해줘야 함.
yarn dls @yarnpkg/sdks vscode
TypeScript 버전을 Workspace 버전으로 할 것인지 묻는 알럿창이 뜨면 허용하고, 그러면 관련된 vscode 설정이 프로젝트 루트에 추가됨.
// settings.json
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"prettier.prettierPath": ".yarn/sdks/prettier/index.cjs",
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
gitignore해주자.
nest g resource
nest g module, controller, service가 아닌 nest g resource
를 활용해
한번에 생성해주자. 이 기능을 활용하면 CRUD Generator
로 CRUD 메소드까지 같이 생성해준다. (학습메모 2)
yarn workspace server nest g resource
프로젝트 루트 폴더는 위 명령을 통해 만들어줄 수 있다.
같은 방식으로 auth, board 모두 생성해줬다.
자동 생성된 모든 test 파일을 test 폴더에 계층(모듈)별로 폴더를 만들어 옮긴 후, jest 설정을 바꾸어 이를 인식할 수 있게 변경해주자.
// package.json
{
...
"jest": {
...
"rootDir": "test",
"testRegex": ".*\\.(e2e-)?spec\\.ts$",
...
}
}
rootDir을 src
에서 test
로,
testRegex는 e2e-spec.ts도 잡아줄 수 있도록 .*\\.spec\\.ts$
에서 .*\\.(e2e-)?spec\\.ts$
로 변경해준다.
// package.json
{
...
"jest": {
...
"verbose": true,
"collectCoverage": true,
...
}
}
추가로 상세 describe와 coverage까지 출력할 수 있도록 verbose
와 collectCoverage
를 true
로 설정해주는 구문을 package.json
에 추가해준다.
확인해보자.
yarn test
루트 디렉토리에서 위에 해당하는 명령은
yarn workspace server test
원래 이랬던 게
아? rootDir이 test
라서 src 내의 파일들을 못잡아줌..
추가적인 설정이 필요하겠다.
고군분투 끝에 collectCoverageFrom
설정에서 src만 잡아주도록 수정해줄 수 있었다.
{
...
"jest": {
...
"rootDir": ".",
...
"collectCoverageFrom": [
"<rootDir>/src/**/*.(t|j)s"
],
"coverageDirectory": "./coverage",
...
}
}
이렇게 이쁘게 나온다! coverage는 앞으로 열심히 올려보자.
최종 package.json은 다음과 같다.
// package.json
{
...
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": ".",
"testRegex": ".*\\.(e2e-)?spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"verbose": true,
"collectCoverage": true,
"collectCoverageFrom": [
"<rootDir>/src/**/*.(t|j)s"
],
"coverageDirectory": "./coverage",
"testEnvironment": "node"
}
}
학습메모 3dm
// auth.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from '../../src/auth/auth.controller';
import { AuthService } from '../../src/auth/auth.service';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [AuthService],
}).compile();
controller = module.get<AuthController>(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
기본 생성되는 코드는 위와 같다. CRUD 메소드에 대한 테스트 코드를 copilot의 도움을 받아 추가해봤다.
// auth.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from '../../src/auth/auth.controller';
import { AuthService } from '../../src/auth/auth.service';
describe('AuthController', () => {
let controller: AuthController;
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [AuthService],
}).compile();
controller = module.get<AuthController>(AuthController);
service = module.get<AuthService>(AuthService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('findAll', () => {
it('should be defined', () => {
expect(controller.findAll).toBeDefined();
});
it('should return an array of auth', async () => {
const result = ['test'];
jest.spyOn(service, 'findAll').mockImplementation((): any => result);
expect(await controller.findAll()).toBe(result);
});
});
describe('findOne', () => {
it('should be defined', () => {
expect(controller.findOne).toBeDefined();
});
it('should return an auth', async () => {
const result = 'test';
jest.spyOn(service, 'findOne').mockImplementation((): any => result);
expect(await controller.findOne('1')).toBe(result);
});
});
describe('create', () => {
it('should be defined', () => {
expect(controller.create).toBeDefined();
});
it('should return an auth', async () => {
const result = 'test';
jest.spyOn(service, 'create').mockImplementationOnce((): any => result);
expect(await controller.create({})).toMatchObject(result);
});
});
describe('update', () => {
it('should be defined', () => {
expect(controller.update).toBeDefined();
});
it('should return an auth', async () => {
const result = 'test';
jest.spyOn(service, 'update').mockImplementation((): any => result);
expect(await controller.update('1', {})).toBe(result);
});
});
describe('remove', () => {
it('should be defined', () => {
expect(controller.remove).toBeDefined();
});
});
});
여기서 mocking되지 않은 service를 또 테스트해봐야 되지 않을까 하는 의문이 생겨, 열띤 토론끝에 오늘 백엔드 멘토님과의 멘토링 시간에 질문을 드려보기로 했다. TDD는 대체 어떤 방식으로 하는 걸까? 지금 Entity 속성이 없는데 이건 어떤 순서로 개발해야 하는거지?
Entity 테스트 코드 작성 -> Entity 구현 -> Controller 테스트 코드 작성 -> Controller 구현 -> Service 테스트 코드 작성 -> Service 구현 -> Repository 테스트 코드 작성
이런 순서가 맞는건지, Repository는 이미 정의된 메소드를 쓰는데 또 이게 실제로 들어가는지 확인하려면 어떤 방식으로 테스트를 해야 하는지가 막막했다.
// 예를 들면 이런식으로 해야하는게 아닐까?
describe('create', () => {
it('should be defined', () => {
expect(controller.create).toBeDefined();
});
it('should return an auth (mock service)', async () => {
const result = 'test';
jest.spyOn(service, 'create').mockImplementationOnce((): any => result);
expect(await controller.create({})).toMatchObject(result);
});
it('should return an auth (real)', async () => {
// given
const user = 'test'; // TODO : 제대로된 User 인스턴스가 들어가야 함.
// when
const result: any = await controller.create(user);
// then
expect(result).toBeInstanceOf(User);
expect(result).toMatchObject(user);
});
it('should assign new id (real)', async () => {
// given
const user = 'test'; // TODO : 제대로된 User 인스턴스가 들어가야 함.
// when
const result: any = await controller.create(user);
expect(result).toHaveProperty('id');
expect(result.id).toBeInstanceOf(Number);
});
});
그래서 여기까지 줄이고 TDD와 동시성 제어와 관련해 질문지를 꼼꼼히 작성하고 멘토링 후 TDD를 계속 진행해보기로 했다!
참고삼아 Repository를 mocking한 Service 유닛테스트 코드 예시까지만 남기고 오늘의 개발기록을 마친다.
// board.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { BoardService } from './board.service';
import { Board } from './entities/board.entity';
describe('BoardService', () => {
let service: BoardService;
let repository: Repository<Board>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
BoardService,
{
provide: getRepositoryToken(Board),
useClass: Repository,
},
],
}).compile();
service = module.get<BoardService>(BoardService);
repository = module.get<Repository<Board>>(getRepositoryToken(Board));
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('findAll', () => {
it('should be defined', () => {
expect(service.findAll).toBeDefined();
});
it('should return an array of boards', async () => {
const board = new Board();
board.id = 1;
board.title = 'test';
board.content = 'test';
board.image = null;
board.star_style = 'test';
board.star_position = 'POINT(0, 0, 0)';
board.author = 'test';
board.created_at = new Date();
board.modified_at = new Date();
const boards = [board];
jest.spyOn(repository, 'find').mockImplementation(async () => boards);
expect(await service.findAll()).toBe(boards);
});
});
});
- [GIT] Conflict(충돌) 났을 때 강제로 Pull 하기.
- NestJS CRUD Generator
- NestJS, TDD로 개발하기
- NestJS Mocking하기
- Repository 테스트하기
-
준섭
-
auth resource 추가 nest g resource로 auth 리소스 추가
근데 이렇게 하고 git status를 쳐보니까
이런 식으로 .yarn 폴더 안에 unplugged 폴더 수정이 많이 일어났다.
그러나 같이 페어 프로그래밍을 하고 있는 재하님은 unplugged 폴더 수정이 일어나지 않았다.
혹시 몰라서 nest 버전도 맞추고, yarn install도 같이 해보고 다시 nest g reousrce를 해보았지만 결과는 역시 달랐다.
이유가 뭐지?
- 해결 다시 처음 상태로 git reset --hard로 돌아감 그 다음 yarn workspace server nest g resource로 auth 모듈 생성 이 상태로 다시 루트 폴더에서 yarn install을 하니 재하님과 git status도 똑같아지고 서버 실행도 잘 되었다. 내가 package/server 폴더까지 들어가서 yarn install을 한 게 잘못이었던 듯 하다
-
board resource 추가
위와 같은 방법으로 추가 완료
-
-
준섭
-
.spec 테스트 파일들 test 폴더로 이동
그러고 yarn workspace server test를 해보려고 하니
다음과 같이 test 파일들을 찾을 수 없다는 안내가 뜸
그 원인을 알아보니 package.json에 jest 설정 중 root 폴더 패스 때문이었음.
package.json의 root 설정이 원래 "src"로 되어있었던 것을 위와 같이 "test"로 수정
그 후 testRegex 설정도 e2e test들은 그냥 .spec이 아닌 .e2e-spec으로 되어있기 때문에 수정해주었음.
이렇게 하고 다시 yarn workspace server test를 해보니 잘 됨!
-
verbose, collectCoverage 설정 추가
그리고 test 실행 시 좀 더 자세한 설명을 추가하는 verbose 옵션과 만든 기능들을 테스트 코드로 얼마나 커버하는지를 알려주는 collectCoverage 옵션을 추가해 주었음.
처음에는 jest.config.json 생성해서 설정을 추가하고 다시 test를 해보았는데, 다음과 같은 오류가 뜸
nest 프로젝트 자체에서 저렇게 따로 config.json을 생성해주는 것이 아니라 package.json의 jest 옵션에서 관리를 해주는 것 같았음.
위와 같이 "verbose" 옵션과 "collecCoverage" 옵션을 추가
테스트가 예쁘게 잘 출력되는 줄 알았으나 coverage 부분이 비어있음..
위와 같이 rootDir을 "."으로 수정하고 collectCoverageFrom으로 커버리지 수집 폴더를 지정해 줌.
그러니 이제야 드디어 테스트가 완벽히 예쁘게 실행 됨^^
- 참고
[https://www.inflearn.com/questions/14613/no-tests-found-exiting-with-code-1-에러](https://www.inflearn.com/questions/14613/no-tests-found-exiting-with-code-1-%EC%97%90%EB%9F%AC)
-
© 2023 debussysanjang
- 🐙 [가은] Three.js와의 설레는 첫만남
- 🐙 [가은] JS로 자전과 공전을 구현할 수 있다고?
- ⚽️ [준섭] NestJS 강의 정리본
- 🐧 [동민] R3F Material 간단 정리
- 👾 [재하] 만들면서 배우는 NestJS 기초
- 👾 [재하] GitHub Actions을 이용한 자동 배포
- ⚽️ [준섭] 테스트 코드 작성 이유
- ⚽️ [준섭] TypeScript의 type? interface?
- 🐙 [가은] 우리 팀이 Zustand를 쓰는 이유
- 👾 [재하] NestJS, TDD로 개발하기
- 👾 [재하] AWS와 NCP의 주요 서비스
- 🐰 [백범] Emotion 선택시 고려사항
- 🐧 [동민] Yarn berry로 모노레포 구성하기
- 🐧 [동민] Vite, 왜 쓰는거지?
- ⚽️ [준섭] 동시성 제어
- 👾 [재하] NestJS에 Swagger 적용하기
- 🐙 [가은] 너와의 추억을 우주의 별로 띄울게
- 🐧 [동민] React로 멋진 3D 은하 만들기(feat. R3F)
- ⚽️ [준섭] NGINX 설정
- 👾 [재하] Transaction (트랜잭션)
- 👾 [재하] SSH 보안: Key Forwarding, Tunneling, 포트 변경
- ⚽️ [준섭] MySQL의 검색 - LIKE, FULLTEXT SEARCH(전문검색)
- 👾 [재하] Kubernetes 기초(minikube), docker image 최적화(멀티스테이징)
- 👾 [재하] NestJS, 유닛 테스트 각종 mocking, e2e 테스트 폼데이터 및 파일첨부
- 2주차(화) - git, monorepo, yarn berry, TDD
- 2주차(수) - TDD, e2e 테스트
- 2주차(목) - git merge, TDD
- 2주차(일) - NCP 배포환경 구성, MySQL, nginx, docker, docker-compose
- 3주차(화) - Redis, Multer 파일 업로드, Validation
- 3주차(수) - AES 암복호화, TypeORM Entity Relation
- 3주차(목) - NCP Object Storage, HTTPS, GitHub Actions
- 3주차(토) - Sharp(이미지 최적화)
- 3주차(일) - MongoDB
- 4주차(화) - 플랫폼 종속성 문제 해결(Sharp), 쿼리 최적화
- 4주차(수) - 코드 개선, 트랜잭션 제어
- 4주차(목) - 트랜잭션 제어
- 4주차(일) - docker 이미지 최적화
- 5주차(화) - 어드민 페이지(전체 글, 시스템 정보)
- 5주차(목) - 감정분석 API, e2e 테스트
- 5주차(토) - 유닛 테스트(+ mocking), e2e 테스트(+ 파일 첨부)
- 6주차(화) - ERD
- 2주차(화) - auth, board 모듈 생성 및 테스트 코드 환경 설정
- 2주차(목) - Board, Auth 테스트 코드 작성 및 API 완성
- 3주차(월) - Redis 연결 후 RedisRepository 작성
- 3주차(화) - SignUpUserDto에 ClassValidator 적용
- 3주차(화) - SignIn시 RefreshToken 발급 및 Redis에 저장
- 3주차(화) - 커스텀 AuthGuard 작성
- 3주차(수) - SignOut시 토큰 제거
- 3주차(수) - 깃헙 로그인 구현
- 3주차(토) - OAuth 코드 통합 및 재사용
- 4주차(수) - NestJS + TypeORM으로 MySQL 전문검색 구현
- 4주차(목) - NestJS Interceptor와 로거
- [전체] 10/12(목)
- [전체] 10/15(일)
- [전체] 10/30(월)
- [FE] 11/01(수)~11/03(금)
- [전체] 11/06(월)
- [전체] 11/07(화)
- [전체] 11/09(목)
- [전체] 11/11(토)
- [전체] 11/13(월)
- [BE] 11/14(화)
- [BE] 11/15(수)
- [FE] 11/16(목)
- [FE] 11/19(일)
- [BE] 11/19(일)
- [FE] 11/20(월)
- [BE] 11/20(월)
- [BE] 11/27(월)
- [FE] 12/04(월)
- [BE] 12/04(월)
- [FE] 12/09(금)
- [전체] 12/10(일)
- [FE] 12/11(월)
- [전체] 12/11(월)
- [전체] 12/12(화)