[크래프톤 정글 3기] 12/31(일) TIL

ClassBinu·2023년 12월 31일
0

크래프톤 정글 3기 TIL

목록 보기
75/120

09:20 스벅 옴!
jest로 테스트 구현해보고, 이력서 다시 정리하기!
React 기본 문법 익히기!

Jest test

    "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);
});

it & describe

테스트 1개는 it로 지정
여러 개의 테스트를 그룹별도 묶을 때는 describe 사용

beforeAll, beforeEach

All은 테스트 시작 전 1회 수행
Each는 it테스트 실행 전 매번 실행

유닛 테스트는 일반적으로 목업 데이터로 한다.
그럼 DB문제는 못 찾지 않나?
그래서 유닛, 통합, 시스템(e2e) 테스트를 종합적으로 구성해야 함.

User 테스트 코드 작성

작동 원리 파악해 보려고 작성해 봄.

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);
  }
}

지금 테스트 코드 자체에 중복이 많고 뭔가 비효율적인 느낌인데..
이건 테스트 코드 정보 더 찾아보면서 보완하기!
우선은 유저쪽만 구현하고 정확한 테스트 구현 방법 찾아서 나머지 모듈도 적용해 보기!

tobo와 toEqual 차이

toEqual()을 사용하면 객체의 내용이 같은지 검증할 수 있습니다. 객체의 구조와 값이 같다면, 서로 다른 인스턴스여도 테스트는 통과합니다.
toBe()는 기본 데이터 타입 또는 같은 객체 인스턴스에 대한 동등성을 검증할 때 사용합니다. 객체의 참조가 완전히 같아야 테스트가 통과합니다.

값만 비교하려면 toEqual을 쓰면 됨. 완전 동일 객체인지 평가할 때는 toBe()를 쓰면 됨.
마치 '=='와 '==='의 관계

프롬프트 엔지니어링

성능 향상을 위한 팁

https://www.youtube.com/watch?v=rMJErYfGLCU

  1. 지시 문장과 참고 문장을 기호로 구분(예. """)
  2. 지시 문장(조건)은 앞 부분, 참고 문장을 뒷 부분
  3. 예시를 들어 설명하기. (말투 등)
  4. 지시가 충분했는지 되묻기(내가 어떤 걸 물어보면 더 나은 답변은 받을 수 있지?)
  5. 쪼개서 지시하기
  6. 단축어 쓰기 (Meta Language Creation 패턴)
  7. RAG(외부 데이터 끌어오기)

리액트

공식 사이트 틱택토 튜토리얼로 기본 문법 익힘.

JSX

jsx는 일종의 syntactic sugar와 같다. 기존에 함수 기반의 리액트 코드를 최대한 html과 비슷하게 작성할 수 있게 한다. 이건 바벨과 같은 트랜스파일러를 거치면 React코드로 변환된다.

JSX에서 class나 for 등을 쓸 수 없는 이유는?
JS에서 이미 선점한 예약어이기 때문임.
class -> className
for -> htmlFor

State & Props

state는 컴포넌트 내부 상태를 나타내는 데이터(객체)
반면에 외부에서 컴포넌트로 전달되는 상태(데이터)를 Props라고 한다.

useState

컴포넌트 내의 상태 변수 선언과 값 변경 제공

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);

react 바인딩

const [minutes, setMinutes] = React.useState();
const onChange = (event) => {
	setMinutes(event.target.value);
};
return (
	<input
  		value={minutes}
		onChange={onChange}
	/>
)

0개의 댓글