TypeScript로 잘 구성된 단위 테스트 작성
40911 단어 typescriptjavascripttestingtutorial
설정 프로젝트
NPM과 Git Versioning을 사용하여 새로운 브랜드 프로젝트를 만들어 봅시다.
mkdir my-project
cd /my-project
git init
npm init
그런 다음 일부 종속 항목을 설치하고 TypeScript를 사용하여 Jest를 실행하기 위해 babel을 사용합니다.
npm install --save sequelize pg pg-hstore
npm install --save-dev typescript ts-node jest babel-jest @types/sequelize @types/jest @babel/preset-typescript @babel/preset-env @babel/core
TypeScript를 사용할 때 TypeScript 파일을 src에서 dist 폴더로 전사하는 방법을 나타내기 위해
tsconfig.json
를 생성해야 합니다.//tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2017",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": false,
"strict": true,
"baseUrl": ".",
"typeRoots": ["node_modules/@types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
그런 다음 프로젝트 폴더에
babel.config.js
를 추가해야 단위 테스트를 직접 실행할 수 있습니다.//babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};
자, 이제 코드 작성을 시작하겠습니다.
코드 작성
모델, 리포지토리, 데이터베이스 라이브러리 및 서비스로 디자인 패턴을 따를 것입니다. 가능한 한 간단하므로 전체 범위를 포함하는 간단한 단위 테스트를 작성할 수 있습니다. 프로젝트 구조는 다음과 같습니다.
my-project/
├──src/
| ├──bookModel.ts
| ├──bookRepo.test.ts
| ├──bookRepo.ts
| ├──bookService.test.ts
| ├──bookService.ts
| └──database.ts
├──babel.config.js
├──package.json
└──tsconfig.json
먼저
database.ts
를 생성해야 합니다. 이것은 Sequelize의 데이터베이스 연결 라이브러리입니다.//database.ts
import { Sequelize } from 'sequelize';
export const db: Sequelize = new Sequelize(
<string>process.env.DB_NAME,
<string>process.env.DB_USER,
<string>process.env.DB_PASSWORD,
{
host: <string>process.env.DB_HOST,
dialect: 'postgres',
logging: console.log
}
);
이제 모델을 정의해 보겠습니다. 모델은 Sequelize의 핵심입니다. 모델은 데이터베이스의 테이블을 나타내는 추상화입니다. Sequelize에서는 Model을 확장하는 클래스입니다. Book Model을 나타내는 Sequelize 확장Class Model을 사용하여 하나의 모델을 생성합니다.
//bookModel.ts
import { db } from './database';
import { Model, DataTypes, Sequelize } from 'sequelize';
export default class Book extends Model {}
Book.init(
{
id: {
primaryKey: true,
type: DataTypes.BIGINT,
autoIncrement: true
},
title: {
type: DataTypes.STRING,
allowNull: false
},
author: {
type: DataTypes.STRING,
allowNull: false
},
page: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
publisher: {
type: DataTypes.STRING
},
quantity: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
created_at: {
type: DataTypes.DATE,
defaultValue: Sequelize.fn('now'),
allowNull: false
},
updated_at: {
type: DataTypes.DATE,
defaultValue: Sequelize.fn('now'),
allowNull: false
}
},
{
modelName: 'books',
freezeTableName: true,
createdAt: false,
updatedAt: false,
sequelize: db
}
);
좋습니다. 다음으로 리포지토리 레이어를 생성합니다. 데이터 액세스를 추상화하기 위한 전략입니다. 모델과 상호 작용하는 여러 가지 방법을 제공합니다.
//bookRepo.ts
import Book from './bookModel';
class BookRepo {
getBookDetail(bookID: number): Promise<Book | null> {
return Book.findOne({
where: {
id: bookID
}
});
}
removeBook(bookID: number): Promise<number> {
return Book.destroy({
where: {
id: bookID
}
});
}
}
export default new BookRepo();
그런 다음 서비스 계층을 생성합니다. 애플리케이션의 비즈니스 로직으로 구성되며 저장소를 사용하여 데이터베이스와 관련된 특정 로직을 구현할 수 있습니다.
separate repository layer and service layer 있는 것이 좋습니다. 별도의 레이어를 사용하면 코드가 더 모듈화되고 비즈니스 로직에서 데이터베이스가 분리됩니다.
//bookService.ts
import BookRepo from './bookRepo';
import Book from './bookModel';
class BookService {
getBookDetail(bookId: number): Promise<Book | null> {
return BookRepo.getBookDetail(bookId);
}
async removeBook(bookId: number): Promise<number> {
const book = await BookRepo.getBookDetail(bookId);
if (!book) {
throw new Error('Book is not found');
}
return BookRepo.removeBook(bookId);
}
}
export default new BookService();
자, 우리는 비즈니스 로직을 완료했습니다. 단위 테스트를 작성하는 방법에 집중하고 싶기 때문에 컨트롤러와 라우터를 작성하지 않습니다.
단위 테스트 작성
이제 리포지토리 및 서비스 레이어에 대한 단위 테스트를 작성합니다. 단위 테스트를 작성하기 위해 AAA (Arrange-Act-Assert) pattern을 사용합니다.
AAA 패턴은 우리가 테스트 방법을 배열, 행동 및 주장의 세 부분으로 나누어야 함을 시사합니다. 그들 각각은 그들이 이름을 딴 부분에 대해서만 책임이 있습니다. 이 패턴을 따르면 코드가 잘 구조화되고 이해하기 쉬워집니다.
단위 테스트를 작성해 봅시다. 우리는 외부 종속성의 동작이나 상태가 아닌 테스트 중인 코드를 격리하고 집중하기 위해 bookModel의 메서드를 조롱할 것입니다. 그런 다음 우리는 같아야 하고, 여러 번 호출되어야 하고, 일부 매개변수와 함께 호출되어야 하는 경우와 같은 일부 경우에 단위 테스트를 주장할 것입니다.
//bookRepo.test.ts
import BookRepo from './bookRepo';
import Book from './bookModel';
describe('BookRepo', () => {
beforeEach(() =>{
jest.resetAllMocks();
});
describe('BookRepo.__getBookDetail', () => {
it('should return book detail', async () => {
//arrange
const bookID = 1;
const mockResponse = {
id: 1,
title: 'ABC',
author: 'John Doe',
page: 1
}
Book.findOne = jest.fn().mockResolvedValue(mockResponse);
//act
const result = await BookRepo.getBookDetail(bookID);
//assert
expect(result).toEqual(mockResponse);
expect(Book.findOne).toHaveBeenCalledTimes(1);
expect(Book.findOne).toBeCalledWith({
where: {
id: bookID
}
});
});
});
describe('BookRepo.__removeBook', () => {
it('should return true remove book', async () => {
//arrange
const bookID = 1;
const mockResponse = true;
Book.destroy = jest.fn().mockResolvedValue(mockResponse);
//act
const result = await BookRepo.removeBook(bookID);
//assert
expect(result).toEqual(mockResponse);
expect(Book.destroy).toHaveBeenCalledTimes(1);
expect(Book.destroy).toBeCalledWith({
where: {
id: bookID
}
});
});
});
});
그런 다음 서비스 계층에 대한 단위 테스트를 작성합니다. 리포지토리 레이어와 동일하게 서비스 레이어 테스트에서 리포지토리 레이어를 모의하여 테스트 중인 코드를 격리하고 집중합니다.
//bookService.test.ts
import BookService from './bookService';
import BookRepo from './bookRepo';
describe('BookService', () => {
beforeEach(() =>{
jest.resetAllMocks();
});
describe('BookService.__getBookDetail', () => {
it('should return book detail', async () => {
//arrange
const bookID = 1;
const mockResponse = {
id: 1,
title: 'ABC',
author: 'John Doe',
page: 1
};
BookRepo.getBookDetail = jest.fn().mockResolvedValue(mockResponse);
//act
const result = await BookService.getBookDetail(bookID);
//assert
expect(result).toEqual(mockResponse);
expect(BookRepo.getBookDetail).toHaveBeenCalledTimes(1);
expect(BookRepo.getBookDetail).toBeCalledWith(bookID);
});
});
describe('BookService.__removeBook', () => {
it('should return true remove book', async () => {
//arrange
const bookID = 2;
const mockBookDetail = {
id: 2,
title: 'ABC',
author: 'John Doe',
page: 1
};
const mockResponse = true;
BookRepo.getBookDetail = jest.fn().mockResolvedValue(mockBookDetail);
BookRepo.removeBook = jest.fn().mockResolvedValue(mockResponse);
//act
const result = await BookService.removeBook(bookID);
//assert
expect(result).toEqual(mockResponse);
// assert BookRepo.getBookDetail
expect(BookRepo.getBookDetail).toHaveBeenCalledTimes(1);
expect(BookRepo.getBookDetail).toBeCalledWith(bookID);
//assert BookRepo.removeBook
expect(BookRepo.removeBook).toHaveBeenCalledTimes(1);
expect(BookRepo.removeBook).toBeCalledWith(bookID);
});
it('should throw error book is not found', () => {
//arrange
const bookID = 2;
const mockBookDetail = null;
const errorMessage = 'Book is not found';
BookRepo.getBookDetail = jest.fn().mockResolvedValue(mockBookDetail);
//act
const result = BookService.removeBook(bookID);
//assert
expect(result).rejects.toThrowError(errorMessage);
expect(BookRepo.getBookDetail).toHaveBeenCalledTimes(1);
expect(BookRepo.getBookDetail).toBeCalledWith(bookID);
});
});
});
좋습니다. 단위 테스트 작성을 완료했습니다.
테스트를 실행하기 전에 다음과 같이 package.json에 스크립트 테스트를 추가합니다.
//package.json
...
"scripts": {
"build": "tsc",
"build-watch": "tsc -w",
"test": "jest --coverage ./src"
},
...
좋습니다. 마침내 터미널에서 다음 명령으로 테스트를 실행할 수 있습니다.
npm test
실행 후, 우리는 단위 테스트가 성공했고 완전히 적용된다는 것을 알려주는 결과를 얻을 것입니다 🎉
아름다운! ✨
연결:
Reference
이 문제에 관하여(TypeScript로 잘 구성된 단위 테스트 작성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/arifintahu/writing-well-structured-unit-test-in-typescript-2hal텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)