TDD은 소프트웨어 개발 방법론 중의 하나로, 선 개발 후 테스트 방식이 아닌 선 테스트 후 개발 방식의 프로그래밍 방법을 말한다. 다시 말해 먼저 자동화된 테스트 코드를 작성한 후 테스트를 통과하기 위한 코드를 개발하는 방식의 개발 방식을 말한다.
- 객체 지향적인 코드 개발
TDD는 코드의 재사용 보장을 명시하므로 TDD를 통한 소프트웨어 개발 시 기능별로 모듈화가 이루어진다. 이는 의존성과 종속성이 낮은 모듈로 조합된 소프트웨어 개발을 가능하게 하며, 필요에 따라 모듈을 추가하거나 제거해도 소프트웨어 전체 구조에 영향을 미치지 않게 된다.
- 설계 수정시간의 단축
테스트코드를 먼저 작성하기 때문에 최초 설계안을 만족하게 하며 입출력 구조와 기능의 정의를 명확하게 하게 되므로 설계의 구조적 문제를 바로 찾아낼 수 있다.
- 유지보수(리팩토링)의 용이성
기본적으로 단위 테스트 기반의 테스트 코드를 작성하기 때문에 추후 문제가 발생하였을 때 각각의 모듈별로 테스트를 진행해보면 문제의 지점을 쉽게 찾을 수 있다.
- 테스트 문서의 대체 가능
대부분의 개발 프로젝트에서 테스트를 진행하는 경우 단순 통합 테스트에 지나지 않는다. TDD를 하게 될 때 테스팅을 자동화시킴과 동시에 더욱 정확한 테스트 근거를 산출해 정의서를 작성할 수 있다.
✅ 파일 생성 : 이름.spect.ts
✅ 테스트 명령어 : yarn test
it('더하기 테스트', () => {
const a = 1;
const b = 2;
expect(a + b).toBe(3);
});
describe('나의 테스트 그룹', () => {
it('더하기 테스트', () => {
const a = 1;
const b = 2;
expect(a + b).toBe(3);
});
it('곱하기 테스트', () => {
const a = 1;
const b = 2;
expect(a * b).toBe(2);
});
});
describe('상품 구매 테스트', () => {
// beforeEach : 각각을 테스트하기 전 실행
// ex) Login
beforeEach(() => {});
it('돈 검증하기', () => {
// 돈이 충분하다고 가정
const result = true;
expect(result).toBe(true);
});
it('상품 구매하기', () => {
// 상품 구매 성공 가정
const result = true;
expect(result).toBe(true);
});
import { ConflictException } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../entities/user.entity';
import { UserService } from '../user.service';
ype MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
describe('UserService', () => {
// userService, userRepository 타입 설정
let userService: UserService;
let userRepository: MockRepository<User>;
beforeEach(async () => {
const userModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useClass: MockUserRepository,
},
],
}).compile();
userService = userModule.get<UserService>(UserService);
userRepository = userModule.get<MockRepository<User>>(
getRepositoryToken(User),
);
});
describe('create', () => {
it('이메일 존재여부 확인', async () => {
const userRepositorySpyFindOne = jest.spyOn(userRepository, 'findOne');
const userRepositorySpySave = jest.spyOn(userRepository, 'save');
const myData = {
email: 'a@a.com',
hashedPwd: '1234',
name: '철수',
age: 13,
};
try {
await userService.create({ ...myData });
} catch (error) {
expect(error).toBeInstanceOf(ConflictException);
}
expect(userRepositorySpyFindOne).toBeCalledTimes(1);
expect(userRepositorySpySave).toBeCalledTimes(0);
});
it('회원 등록 잘 됐는지 검증!!', async () => {
const userRepositorySpyFindOne = jest.spyOn(userRepository, 'findOne');
const userRepositorySpySave = jest.spyOn(userRepository, 'save');
const myData = {
email: 'bbb@bbb.com',
hashedPwd: '1234',
name: '철수',
age: 13,
};
const myResultData = {
email: 'bbb@bbb.com',
password: '1234',
name: '철수',
age: 13,
};
const result = await userService.create({ ...myData });
// 객체 or 배열을 확인하기 위해서 toStrictEqual을 사용
// 객체와 배열은 내용이 같아보여도 같지 않다.
expect(result).toStrictEqual(myResultData);
expect(userRepositorySpyFindOne).toBeCalledTimes(1);
expect(userRepositorySpySave).toBeCalledTimes(1);
});
});
describe('findOne', () => {});
});
class MockUserRepository {
mydb = [
{
email: 'a@a.com',
hashedPwd: '0000',
name: '짱구',
age: 5,
},
];
findOne({ email }) {
const users = this.mydb.filter((el) => {
el.email === email;
});
if (users.length) return users[0];
return null;
}
save({ email, password, name, age }) {
this.mydb.push({
email,
hashedPwd: password,
name,
age,
});
return { email, password, name, age };
}
}
참조