코딩기록

항해 29일) Nods.js 3-3심화주차- 테스트 코드 방법설명 본문

항해99/챕터3 주특기 Node.js

항해 29일) Nods.js 3-3심화주차- 테스트 코드 방법설명

뽀짝코딩 2022. 2. 7. 12:19
728x90

index.spec.js 에 테스트 코드 메시지를 주고 

index.js에 메소드를 작성해 테스트 코드 메시지가 통과되게 하는 게 이번 주차 과제이다. 

 

코드자체가 이해가 안되서 계속 js기초 강의를 봤었다. class, constructor, 상속 등을 봤는데 보다보니 얼추, 알것도 같다

 

주어진 코드

 index.spec.js 

더보기

 

// const { Site, Board, Article, Comment } = require('.');
const { Site } = require('.');

describe('Site 요구사항 테스트', () => {
    test('Site는 n개 이상 생성 할 수 있다.', () => {
        expect(() => {
                        //new가 붙으면 객체 생성자(constructor) 함수
            const _site1 = new Site();
            const _site2 = new Site();
        }).not.toThrow();
    });

    test('Site에는 Board를 추가하고 추가된 Board를 조회할 수 있다.', () => {
        const mySite = new Site();
        const noticeBoard = new Board('공지사항');

        mySite.addBoard(noticeBoard);

        expect(mySite.findBoardByName('공지사항')).toEqual(noticeBoard);
    });

    test('하나의 Site에 동일한 이름의 Board를 추가할 수 없다.', () => {
        const mySite = new Site();
        const noticeBoard1 = new Board('공지사항');
        const noticeBoard2 = new Board('공지사항');

        mySite.addBoard(noticeBoard1);

        expect(() => {
            mySite.addBoard(noticeBoard2);
        }).toThrow();
    });

    test('Board는 n개 이상 추가 할 수 있다.', () => {
        const mySite = new Site();
        const noticeBoard = new Board('공지사항');
        const faqBoard = new Board('FAQ');

        expect(() => {
            mySite.addBoard(noticeBoard);
            mySite.addBoard(faqBoard);
        }).not.toThrow();

        expect(mySite.boards).toEqual([noticeBoard, faqBoard]);
    });
});

describe('Board 요구사항 테스트', () => {
    /**
     * @type {Site}
     */
    let mySite;

    beforeEach(() => {
        // NOTE: Reset `mySite`
        mySite = new Site();
    });

    test('Board는 name 데이터를 포함해야 하며 null 또는 빈 문자열("")은 허용하지 않는다.', () => {
        expect(() => {
            const _board = new Board('공지사항');
        }).not.toThrow();

        expect(() => {
            const _board = new Board('');
        }).toThrow();

        expect(() => {
            const _board = new Board(null);
        }).toThrow();
    });

    test('Site 에 추가된 Board만 사용 가능한 것으로 간주하며 사용 불가능한 Board에는 Article을 추가할 수 없다.', () => {
        const addedBoard = new Board('사이트에 추가된 게시판');
        const notAddedBoard = new Board('사이트에 추가되지 않은 게시판');

        mySite.addBoard(addedBoard);

        expect(() => {
            const article = new Article({
                subject: '글 제목',
                content: '내용',
                author: '작성자',
            });
            addedBoard.publish(article);
        }).not.toThrow();

        expect(() => {
            const article = new Article({
                subject: '글 제목2',
                content: '내용',
                author: '작성자',
            });
            notAddedBoard.publish(article);
        }).toThrow();
    });

    test('Board에 Article을 추가할 때 Article에 ID를 자동 생성해서 부여해야 한다.', () => {
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        // 규칙은 ${board.name}-${랜덤 값} 를 따른다.
        expect(article.id.startsWith('공지사항-')).toBe(true);
    });

    test('Board에 Article을 추가할 때 Article에 작성 일자가 들어가야 한다.', () => {
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        // createdDate가 저장되는 형식은 ISO 8601을 따른다.
        expect(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(article.createdDate)).toBe(true);
    });

    test('Article 은 n개 이상 추가 할 수 있다.', () => {
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        const article2 = new Article({
            subject: '두번째 공지사항입니다.',
            content: 'DB나 웹서버를 이용할 필요는 없습니다.',
            author: '강승현',
        });

        noticeBoard.publish(article);

        expect(() => {
            noticeBoard.publish(article2);
        }).not.toThrow();
    });

    test('작성된 Article 목록을 조회 할 수 있어야 한다.', () => {
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        const article2 = new Article({
            subject: '두번째 공지사항입니다.',
            content: 'DB나 웹서버를 이용할 필요는 없습니다.',
            author: '강승현',
        });
        noticeBoard.publish(article2);

        expect(noticeBoard.getAllArticles()).toEqual([article, article2]);
    });
});

describe('Article 요구사항 테스트', () => {
    /**
     * @type {Site}
     */
    let mySite;

    beforeEach(() => {
        // NOTE: Reset `mySite`
        mySite = new Site();
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);
    });

    test('Article은 subject, content, author 3개의 데이터를 포함해야 하며 null 또는 빈 문자열("")은 허용하지 않는다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');

        expect(() => {
            const article = new Article({
                subject: '첫번째 공지사항입니다.',
                content: '테스트 코드는 수정하면 안됩니다.',
                author: '강승현',
            });
            noticeBoard.publish(article);
        }).not.toThrow();

        expect(() => {
            const article2 = new Article({
                subject: null,
                content: null,
                author: '',
            });
            noticeBoard.publish(article2);
        }).toThrow();
    });

    test('Board에 추가된 Article만 사용 가능한 것으로 간주하며 사용 불가능한 Article에는 Comment를 추가할 수 없다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');

        const publishedArticle = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(publishedArticle);

        const draftArticle = new Article({
            subject: '아직 게시하지 않은 공지사항입니다.',
            content: '댓글을 달 수 없어야 합니다',
            author: '강승현',
        });

        expect(() => {
            const comment = new Comment({
                content: '넵!',
                author: '댕댕이',
            });
            publishedArticle.reply(comment);
        }).not.toThrow();

        expect(() => {
            const comment = new Comment({
                content: '넵!',
                author: '댕댕이',
            });
            draftArticle.reply(comment);
        }).toThrow();
    });

    test('Article에 Comment를 추가할 때 Comment에 작성 일자가 들어가야 한다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        const comment = new Comment({
            content: '넵!',
            author: '댕댕이',
        });
        article.reply(comment);

        // createdDate가 저장되는 형식은 ISO 8601을 따른다.
        expect(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(comment.createdDate)).toBe(true);
    });

    test('Comment는 n개 이상 추가 할 수 있다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        const comment = new Comment({
            content: '넵!',
            author: '댕댕이',
        });
        const comment2 = new Comment({
            content: '네넵!',
            author: '냥냥이',
        });

        expect(() => {
            article.reply(comment);
            article.reply(comment2);
        }).not.toThrow();
    });

    test('작성된 Comment 목록을 조회 할 수 있어야 한다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);

        const comment = new Comment({
            content: '넵!',
            author: '댕댕이',
        });
        const comment2 = new Comment({
            content: '네넵!',
            author: '냥냥이',
        });
        article.reply(comment);
        article.reply(comment2);

        expect(article.getAllComments()).toEqual([comment, comment2]);
    });
});

describe('Comment 요구사항 테스트', () => {
    /**
     * @type {Site}
     */
    let mySite;

    beforeEach(() => {
        // NOTE: Reset `mySite`
        mySite = new Site();
        const noticeBoard = new Board('공지사항');
        mySite.addBoard(noticeBoard);

        const article = new Article({
            subject: '첫번째 공지사항입니다.',
            content: '테스트 코드는 수정하면 안됩니다.',
            author: '강승현',
        });
        noticeBoard.publish(article);
    });

    test('Comment는 content, author 2개의 데이터를 포함해야 하며 null 또는 빈 문자열("")은 허용하지 않는다.', () => {
        const noticeBoard = mySite.findBoardByName('공지사항');
        const [article] = noticeBoard.getAllArticles();

        expect(() => {
            const comment = new Comment({
                content: '댓글1111',
                author: '강승현',
            });
            article.reply(comment);
        }).not.toThrow();

        expect(() => {
            const comment = new Comment({
                content: null,
                author: '',
            });
            article.reply(comment);
        }).toThrow();
    });
});

 

 index.js 

더보기
class Site {

}

class Board {

}

class Article {

}

class Comment {

}

module.exports = {
    Site,
    Board,
    Article,
    Comment,
};

npm install 설치

npm install

npm init 설치

npm init

jest 설치

npm i jest -D

 

package.json에서 jest 수정

"test": "jest" 

그리고 테스트 코드를 돌릴때는 터미널에 

npm test

입력한다.

 

테스트 코드 작성 패턴

test("테스트 설명", () => {
  expect("검증 대상").toXxx("기대 결과");
});

toXxx 부분에서 사용되는 함수를 흔히 Test Mathcher라고 하는데 위에서 사용된 toBe() 함수는 숫자나 문자와 같은 객체가 아닌 기본형(primitive) 값을 비교할 때 사용된다.

 


 

이 과제에서 중요한 것은  index.spec.js 에서 사용된 함수들 즉, Test Mathcher 이다.

describe('Site 요구사항 테스트', () => {
    test('Site는 n개 이상 생성 할 수 있다.', () => {
        expect(() => {
                        //new가 붙으면 객체 생성자(constructor) 함수
            const _site1 = new Site();
            const _site2 = new Site();
        }).not.toThrow();
    });

그래서 Test Mathcher들을 살펴봤다. 그래야 index.js에 어떤 메소드를 작성할지 방향을 정할 수 있다.

 

.not.toThrow()

.toThrow()

 

 index.spec.js  에서 사용된 Test Mathcher는 위 두 가지가 전부다. 

그럼 이제 toThrow()를 살펴보자.

 

toThrow()

예외

특정 함수가 호출될 때 예외 발생 여부를 테스트하는 함수. toThrow() 함수는 인자도 받는데 문자열을 넘기면 예외 메세지를 비교하고 정규식을 넘기면 정규식 체크를 해준다.

 

etUser() 함수가 음수 아이디가 들어왔을 경우, 예외를 던진다.

function getUser(id) {
  if (id <= 0) throw new Error("Invalid ID");
  return {
    id,
    email: `user${id}@test.com`,
  };
}

 테스트 코드를 작성해서 실행해보면 다음과 같이 테스트가 실패하게 된다.

test("throw when id is non negative", () => {
  expect(() => getUser(-1)).toThrow();
  expect(() => getUser(-1)).toThrow("Invalid ID");
});

 

아래는 팀원의 설명이 적혀있는 사진이다. 위 index.spec.js 코드중 Site요구사항 테스트 코드의 index.js코드 설명이다.

 

 index.js 

class Site {
    constructor() {
        this.boards = [];
    } 

    addBoard(board) {
        const findBoard = this.boards.findIndex((i) => i.name === board.name);
        if (-1 === findBoard) {
            board.addedBoard = true;
            this.boards.push(board);
        } else {
            throw new Error('There is already the same board name.');
        }
    }

    findBoardByName(board) {
        const findBoard = this.boards.findIndex((i) => i.name === board);
        return this.boards[findBoard];
    }
}

class Board {
    constructor(board) {
        if (board === '' || board === null) {
            throw new Error('You must give board name');
        }
        this.addedBoard = false;
        this.name = board;
        this.article = [];
    }

    publish(article) {
        if (!this.addedBoard) {
            throw new Error('Please register the board on the site');
        } else {
            article.id = `${this.name}-${Math.random()}`;
            article.createdDate = new Date().toISOString();
            article.comment = [];
            this.article.push(article);
        }
    }

    getAllArticles() {
        return this.article;
    }
}

 

 

반응형
Comments