카운터 데이터는 돔(DOM)에 반영되어야 한다.
이 역할을 하는 ClickCountView 모듈을 만들자.
데이터를 출력하고 이벤트 핸들러를 바인딩하는 일을 담당할 것이다.
첫번째 스펙
"ClickCountView 모듈의 updateView()는 카운트 값을 출력한다"
그런데..
데이터를 조회할 ClickCounter를 어떻게 얻지?
게다가 데이터를 출력할 돔 엘레먼트는 어떻게 테스트하지?
주입하자!
ClickCounter는 객체를 만들어 파라미터로 전달 받자
데이터를 출력할 돔 엘레멘트로 만들어 전달받자
TDD 방식으로 사고하다 보면
이런식으로 필요한 모듈을 주입받아 사용하는 경향이 생긴다.
하나의 기능 단위로 모듈을 분리할 수 있기 때문에 단일 책임 원칙을 지킬 수 있다.
Red
describe('App.ClickCountView', () => {
let clickCounter, updateEl, view;
beforeEach(() => {
clickCounter = App.ClickCounter();
updateEl = document.createElement('span');
view = App.ClickCountView(clickCounter, updateEl);
});
describe('updateView()', () => {
it('ClickCounter의 getValue(0 값을 출력한다.', () => {
const counterValue = clickCounter.getValue;
view.updateView();
expect(updateEl.innerHTML).toBe(counterValue.toString());
});
});
});
ClickCountView(clickCounter, updateEl) 객체를 파라미터로 전달
Green
var App = App || {}
App.ClickCountView = (clickCounter, updateEl) => {
return {
updateView() {
updateEl.innerHTML = clickCounter.getValue();
}
}
}
ClickCountView에 의존성 주입이 되었는지 체크
Red
it('clickCounter를 주입하지 않으면 에러를 던진다', () => {
const clickCounter = null;
const updateEl = document.createElement('span');
const actual = () => App.ClickCountView(clickCounter, updateEl);
expect(actual).toThrowError();
})
it('updateEl 주입하지 않으면 에러를 던진다', () => {
clickCounter = App.ClickCounter();
updateEl = null;
const actual = () => App.ClickCountView(clickCounter, updateEl);
expect(actual).toThrowError();
});
Green
if (!clickCounter) throw Error('clickCounter');
if (!updateEl) throw Error('updateEl');
두번째 스펙
"ClickCountView 모듈의 increaseAndUpdateView()는 카운트 값을 증가하고 그 값을 출력한다."
두개의 기능이기 때문에(카운트 값 증가, 그 값을 출력) 분리해서 테스트 하기
테스트 더블
단위 테스트 패턴으로, 테스트하기 곤란한 컴포넌트를 대체하여 테스트 하는 것.
특정한 동작을 흉내만 낼 뿐이지만 테스트 하기에는 적합하다.
종류 : 더미, 스텁, 스파이, 페이크, 목
자스민에서는 테스트 더블을 스파이스(spies)라고 부른다
spyOn(MyApp, 'foo') // MyApp에 foo라는 함수를 감시하겠다
expect(Myapp.foo).toHaveBeemCalled() // foo 함수가 실행되었는지 체크한다
Red
describe("increaseAndUpdateView()", () => {
it("ClickCounter의 increase를 실행한다.", () => {
spyOn(clickCounter, 'increase')
view.increaseAndUpdateView()
expect(clickCounter.increase).toHaveBeenCalled()
});
it("updateView를 실행한다.", () => {
spyOn(view, 'updateView')
view.increaseAndUpdateView();
expect(view.updateView).toHaveBeenCalled();
});
});
Green
var App = App || {}
App.ClickCountView = (clickCounter, updateEl) => {
if (!clickCounter) throw Error('clickCounter');
if (!updateEl) throw Error('updateEl');
return {
updateView() {
updateEl.innerHTML = clickCounter.getValue();
},
increaseAndUpdateView() {
clickCounter.increase();
this.updateView();
}
}
}
세번째 스펙
"클릭 이벤트가 발생하면 increaseAndUpdateView()를 실행한다"
Red
it("클릭 이벤트가 발생하면 increaseAndUpdateView를 실행한다", () => {
spyOn(view, 'increaseAndUpdateView');
triggerEl.click();
expect(view.increaseAndUpdateView).toHaveBeenCalled();
});
triggerEl 생성
Green
var App = App || {}
App.ClickCountView = (clickCounter, options) => {
if (!clickCounter) throw Error(App.ClickCountView.message.noClickCounter);
if (!options.updateEl) throw Error(App.ClickCountView.message.noUpdateEl);
const view = {
updateView() {
options.updateEl.innerHTML = clickCounter.getValue();
},
increaseAndUpdateView() {
clickCounter.increase();
this.updateView();
}
}
options.triggerEl.addEventListener('click', () => {
view.increaseAndUpdateView();
})
return view;
}
App.ClickCountView.message = {
noClickCounter: 'clickCounter를 주입해야 합니다.',
noUpdateEl : 'updateEl을 주입해야 합니다.'
}
화면에 붙여보기
index.html
<html>
<body>
<span id="counter-display"></span>
<button id="btn-increase">Increase</button>
<script src="ClickCounter.js"></script>
<script src="ClickCountView.js"></script>
<script>
(() => {
const clickCounter = App.ClickCounter();
const updateEl = document.querySelector("#counter-display");
const triggerEl = document.querySelector("#btn-increase");
const view = App.ClickCountView(clickCounter, {updateEl, triggerEl});
view.updateView();
})()
</script>
</body>
</html
Reference
견고한 JS 소프트웨어 만들기 - 인프런 | 강의
같은 기능을 만들더라도 자바스크립트 문법을 이제 막 뗀 주니어 개발자와 경험 많은 시니어 개발자의 코드는 상당히 다릅니다. 물론 결과물은 같더라도 말이죠. 후자의 코드가 인정받는 이유
www.inflearn.com
'개발 > JavaScript' 카테고리의 다른 글
[Javascript] 이벤트 위임(Event delegation) (0) | 2022.04.11 |
---|---|
[Vanilla Javascript] Jasmine을 이용해 테스트 코드 작성해보기 -3 (0) | 2022.04.10 |
[Vanilla Javascript] Jasmine을 이용해 테스트 코드 작성해보기 -1 (0) | 2022.04.10 |
[Javascript] Closure 클로저 (0) | 2022.03.30 |
[Javascript] module export, import, 모듈 이름 수정 (0) | 2022.03.25 |