09:20 스벅 옴!
jest로 테스트 구현해보고, 이력서 다시 정리하기!
React 기본 문법 익히기!
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
npm test를 하면 소스코드를 순회하면서 spec.ts(또는 test.ts) 파일을 실행시킴.
it('더하기 테스트', () => {
const a = 1;
const b = 2;
expect(a + b).toBe(3);
});
테스트 1개는 it로 지정
여러 개의 테스트를 그룹별도 묶을 때는 describe 사용
All은 테스트 시작 전 1회 수행
Each는 it테스트 실행 전 매번 실행
유닛 테스트는 일반적으로 목업 데이터로 한다.
그럼 DB문제는 못 찾지 않나?
그래서 유닛, 통합, 시스템(e2e) 테스트를 종합적으로 구성해야 함.
작동 원리 파악해 보려고 작성해 봄.
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
const mockUsersService = {
create: jest.fn((dto) => {
return { id: 1, ...dto };
}),
findAll: jest.fn(),
findOne: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
};
describe('UsersController', () => {
let controller: UsersController;
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [{ provide: UsersService, useValue: mockUsersService }],
}).compile();
controller = module.get<UsersController>(UsersController);
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should create a user and return the created data', async () => {
const createUserDto = {
email: 'user@example.com',
password: 'password123',
};
const result = await controller.create(createUserDto);
expect(service.create).toHaveBeenCalledWith(createUserDto);
expect(result).toEqual({ id: 1, ...createUserDto });
});
});
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
const mockUsersService = {
create: jest.fn((dto) => {
return { _id: 1, ...dto };
}),
findAll: jest.fn(() => {
return [
{ _id: 1, email: 'test1@gmail.com', password: 'password123' },
{ _id: 2, email: 'test2@gmail.com', password: 'password456' },
{ _id: 3, email: 'test3@gmail.com', password: 'password789' },
];
}),
findOne: jest.fn((id: string) => {
return { _id: id, email: 'test1@gmail.com', password: 'password123' };
}),
update: jest.fn((id: string, dto) => {
return { _id: id, ...dto };
}),
remove: jest.fn((id: string) => {
return { _id: id };
}),
};
describe('UsersController', () => {
let controller: UsersController;
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [{ provide: UsersService, useValue: mockUsersService }],
}).compile();
controller = module.get<UsersController>(UsersController);
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should create a user and return the created data', async () => {
const createUserDto = {
email: 'user@example.com',
password: 'password123',
};
const result = await controller.create(createUserDto);
expect(service.create).toHaveBeenCalledWith(createUserDto);
expect(result).toEqual({ _id: 1, ...createUserDto });
});
it('should return an array of users', async () => {
const result = await controller.findAll();
expect(service.findAll).toHaveBeenCalled();
expect(result).toEqual([
{ _id: 1, email: 'test1@gmail.com', password: 'password123' },
{ _id: 2, email: 'test2@gmail.com', password: 'password456' },
{ _id: 3, email: 'test3@gmail.com', password: 'password789' },
]);
});
it('should return a user by id', async () => {
const result = await controller.findOne('1');
expect(service.findOne).toHaveBeenCalledWith('1');
expect(result).toEqual({
_id: '1',
email: 'test1@gmail.com',
password: 'password123',
});
});
it('should update a user by id and return the updated data', async () => {
const updateUserDto = {
email: 'test1@gmail.com',
password: 'password456',
};
const result = await controller.update('1', updateUserDto);
expect(service.update).toHaveBeenCalledWith('1', updateUserDto);
expect(result).toEqual({ _id: '1', ...updateUserDto });
});
it('should remove a user by id and return the removed data', async () => {
const result = await controller.remove('1');
expect(service.remove).toHaveBeenCalledWith('1');
expect(result).toEqual({ _id: '1' });
});
});
import { CreateUserDto } from './dto/create-user.dto';
import { Injectable } from '@nestjs/common';
import { UpdateUserDto } from './dto/update-user.dto';
import { UsersMongoRepository } from './users.repository';
@Injectable()
export class UsersService {
constructor(private readonly usersRepository: UsersMongoRepository) {}
async create(createUserDto: CreateUserDto) {
return await this.usersRepository.create(createUserDto);
}
async findAll() {
return await this.usersRepository.findAll();
}
async findOne(id: string) {
return await this.usersRepository.findOne(id);
}
async findOneByField(field: string, value: string) {
return await this.usersRepository.findOneByField(field, value);
}
async update(id: string, updateUserDto: UpdateUserDto) {
return await this.usersRepository.update(id, updateUserDto);
}
async remove(id: string) {
return await this.usersRepository.remove(id);
}
}
지금 테스트 코드 자체에 중복이 많고 뭔가 비효율적인 느낌인데..
이건 테스트 코드 정보 더 찾아보면서 보완하기!
우선은 유저쪽만 구현하고 정확한 테스트 구현 방법 찾아서 나머지 모듈도 적용해 보기!
toEqual()을 사용하면 객체의 내용이 같은지 검증할 수 있습니다. 객체의 구조와 값이 같다면, 서로 다른 인스턴스여도 테스트는 통과합니다.
toBe()는 기본 데이터 타입 또는 같은 객체 인스턴스에 대한 동등성을 검증할 때 사용합니다. 객체의 참조가 완전히 같아야 테스트가 통과합니다.
값만 비교하려면 toEqual을 쓰면 됨. 완전 동일 객체인지 평가할 때는 toBe()를 쓰면 됨.
마치 '=='와 '==='의 관계
https://www.youtube.com/watch?v=rMJErYfGLCU
공식 사이트 틱택토 튜토리얼로 기본 문법 익힘.
jsx는 일종의 syntactic sugar와 같다. 기존에 함수 기반의 리액트 코드를 최대한 html과 비슷하게 작성할 수 있게 한다. 이건 바벨과 같은 트랜스파일러를 거치면 React코드로 변환된다.
JSX에서 class나 for 등을 쓸 수 없는 이유는?
JS에서 이미 선점한 예약어이기 때문임.
class -> className
for -> htmlFor
state는 컴포넌트 내부 상태를 나타내는 데이터(객체)
반면에 외부에서 컴포넌트로 전달되는 상태(데이터)를 Props라고 한다.
컴포넌트 내의 상태 변수 선언과 값 변경 제공
import React, { useState } from 'react';
function ExampleComponent() {
// 여기서 useState를 호출합니다.
const [stateValue, setStateValue] = useState(initialValue);
// ...
}
중요한 개념
React의 setState 함수나 Hooks의 useState에서 제공하는 set 함수를 사용하여 상태(state)의 값을 변경하면, React는 해당 컴포넌트를 재렌더링합니다. 하지만 React는 효율적인 DOM 업데이트를 위해 '재조정(reconciliation)' 알고리즘을 사용합니다. 이 알고리즘은 변경된 상태에 따라 실제 DOM에 반영해야 하는 변경 사항만을 계산하여, 실제 DOM을 최소한으로 업데이트합니다. 즉, 상태 변경이 발생하면 전체 컴포넌트가 아니라 변경된 부분만이 효율적으로 업데이트됩니다.
// 비추천, counter값이 최신임을 보장하지 않음.
setCounter(counter + 1)
// 콜백 함수 활용, current값을 보장함.
// current는 현재 최신의 값을 받음.
setCounter((current) => current + 1);
const [minutes, setMinutes] = React.useState();
const onChange = (event) => {
setMinutes(event.target.value);
};
return (
<input
value={minutes}
onChange={onChange}
/>
)