인프런에서 견고한 JS 소프트웨어 만들기를 수강하며 적은 강의노트입니다.
보다 자세한 내용은 강의를 통해 확인하실 수 있습니다.
클릭카운터 모듈 - 스펙1
전역공간에 있는 counter
변수를 ClickCounter
안에서 관리
ClickCounter
모듈의 getValue()
는 카운터 값을 반환한다.
테스트 코드 작성 (적색단계)
xxxxxxxxxx
describe('getValue()', ()=> {
it('초기값이 0인 카운터 값을 반환한다', ()=> {
const counter = App.ClickCounter()
expect(counter.getValue()).toBe(0)
})
})
모듈 생성 (녹색단계)
xxxxxxxxxx
var App = App || {}
App.ClickCounter = () => {
return {
getValue() {
return 0
}
}
}
상수 변경 (리팩터단계)
xxxxxxxxxx
var App = App || {}
App.ClickCounter = () => {
let value = 0
return {
getValue() {
return value
}
}
}
클릭카운터 모듈 - 스펙2
테스트 코드 작성 (적색단계)
xxxxxxxxxx
describe('increase()', ()=> {
it('카운터를 1 올린다', ()=> {
// 준비
const counter = App.ClickCounter()
// 실행
counter.increase()
// 단언
expect(counter.getValue()).toBe(1)
})
})
모듈 생성 (녹색단계)
xxxxxxxxxx
var App = App || {}
App.ClickCounter = () => {
let value = 0
return {
getValue() {
return value
},
increase() {
value++
}
}
}
중복코드를 제거하기 위한 재스민 함수
xxxxxxxxxx
describe(() => {
beforeEach(()=> {
// 1
})
afterEach(() => {
// 3
})
it(() => {
// 2
})
})
변경결과
xxxxxxxxxx
describe('App.ClickCounter', ()=> {
let counter
beforeEach(() => {
counter = App.ClickCounter()
})
describe('getValue()', ()=> {
it('초기값이 0인 카운터 값을 반환한다', ()=> {
expect(counter.getValue()).toBe(0)
})
})
describe('increase()', ()=> {
it('카운터를 1 올린다', ()=> {
// 실행
counter.increase()
// 단언
expect(counter.getValue()).toBe(1)
})
})
})
초기값이 0이 아닌경우를 수정
xxxxxxxxxx
describe('increase()', ()=> {
it('카운터를 1 올린다', ()=> {
const initValue = counter.getValue()
// 실행
counter.increase()
// 단언
expect(counter.getValue()).toBe(initValue + 1)
})
})
클릭카운터뷰 모듈 - 스펙1
카운터 데이터는 DOM
에 반영되어야 함
데이터를 출력하고 이벤트 핸들러를 바안딩하는 일을 담당
ClickCounterView
모듈의 updateView()
는 카운트 값을 출력
테스트 코드 작성 (적색단계)
xxxxxxxxxx
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() 값을 출력한다', ()=> {
const counterValue = clickCounter.getValue()
view.updateView()
// 단언
expect(updateEl.innerHTML).toBe(counterValue.toString())
})
})
})
모듈 생성 (녹색단계)
xxxxxxxxxx
var App = App || {}
App.ClickCountView = (clickCounter, updateEl) => {
return {
updateView() {
updateEl.innerHTML = clickCounter.getValue()
}
}
}
ClickCountView에 의존성 주입이 되었나...?
테스트 코드
xxxxxxxxxx
it('clickCounter를 주입하지 않으면 에러를 던진다.', () => {
const clickCounter = null
const updateEl = document.createElement('span')
const actual = () => App.ClickCountView(clickCounter, updateEl)
expect(actual).toThrowError()
})
it('updateEl를 주입하지 않으면 에러를 던진다.', () => {
const clickCounter = App.ClickCounter()
const updateEl = null
const actual = () => App.ClickCountView(clickCounter, updateEl)
expect(actual).toThrowError()
})
모듈 수정
xxxxxxxxxx
var App = App || {}
App.ClickCountView = (clickCounter, updateEl) => {
if(!clickCounter) throw Error('clickCounter')
if(!updateEl) throw Error('clickCounter')
return {
updateView() {
updateEl.innerHTML = clickCounter.getValue()
}
}
}
클릭카운터뷰 모듈 - 스펙2
ClickCountView
모듈의 increaseAndUpdateView()
는 카운트 값을 증가하고 그 값을 출력한다.
ClickCounter
의increase
함수를 실행한다.updateView
함수를 실핸한다.
테스트 더블(스파이스, spies)
단위 테스트 패턴으로, 테스트하기 곤란한 컴포넌트를 대체하여 테스트하는 것으로 특정한 동작을 흉내만 낼뿐이지만 테스트 하기에는 적합하다.
- Dummy: 인자를 체우기 위해 사용.
- Sturb: 더미를 개선하여 실제 동작하게끔 만든 것. (리턴값을 하드코딩)
- Spy: 스텁과 유사. 내부적으로 기록을 남기는 추가기능.
- Fake: 스텁에서 발전한 실제 코드, 운영에서는 사용할 수 없음. (실제 값을 리턴)
- Mock: Dummy, Sturb, Spy를 혼합한 형태.
재스민에서 사용법
xxxxxxxxxx
// MyApp 모듈의 foo 함수를 감시하도록 설정한다.
spyOn(MyApp, 'foo')
// 특정 행동을 한 뒤
bar()
// 감시한 함수가 실행되었는지 체크한다.
expect(MyApp.foo).toHaveBeenCalled()
// 즉, bar() 함수가 MyApp.foo() 함수를 실행하는지 검증하는 코드이다.
테스트 코드 작성 (적색단계)
xxxxxxxxxx
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()
})
})
모듈생성 (녹색단계)
xxxxxxxxxx
var App = App || {}
App.ClickCountView = (clickCounter, updateEl) => {
if (!clickCounter) throw new Error(App.ClickCountView.messages.noClickCounter)
if (!updateEl) throw new Error(App.ClickCountView.messages.noUpdateEl)
return {
updateView() {
updateEl.innerHTML = clickCounter.getValue()
},
increaseAndUpdateView() {
clickCounter.increase()
this.updateView()
}
}
}
App.ClickCountView.messages = {
noClickCounter: 'clickCount를 주입해야 합니다',
noUpdateEl: 'updateEl를 주입해야 합니다'
}
클릭카운터뷰 모듈 - 스펙3
클릭 이벤트가 발생하면 increaseAndUpdateView()
를 실행한다.
테스트 코드 작성(적색단계)
increaseAndUpdateView
를 바인딩할 돔 엘리먼트(triggerEl
)을 주입받자
xxxxxxxxxx
it('클릭 이벤트가 발생하면 increseAndUpdateView를 실행한다', ()=> {
// 준비
spyOn(view, 'increaseAndUpdateView')
// click!
triggerEl.click()
// 단언
expect(view.increaseAndUpdateView).toHaveBeenCalled()
})
모듈생성 (녹색단계)
xxxxxxxxxx
var App = App || {}
App.ClickCountView = (clickCounter, options) => {
if (!clickCounter) throw new Error(App.ClickCountView.messages.noClickCounter)
if (!options.updateEl) throw new Error(App.ClickCountView.messages.noUpdateEl)
if (!options.triggerEl) throw new Error(App.ClickCountView.messages.noTriggerEl)
const view = {
updateView() {
options.updateEl.innerHTML = clickCounter.getValue()
},
increaseAndUpdateView() {
clickCounter.increase()
this.updateView()
}
}
options.triggerEl.addEventListener('click', () => {
view.increaseAndUpdateView()
})
return view
}
App.ClickCountView.messages = {
noClickCounter: 'clickCount를 주입해야 합니다',
noUpdateEl: 'updateEl를 주입해야 합니다',
noTriggerEl: 'triggerEl를 주입해야 합니다.',
}
'FrontEnd' 카테고리의 다른 글
견고한 JS 소프트웨어 만들기 강의노트5 (0) | 2019.09.29 |
---|---|
견고한 JS 소프트웨어 만들기 강의노트4 (0) | 2019.09.29 |
견고한 JS 소프트웨어 만들기 강의노트3 (0) | 2019.09.15 |
견고한 JS 소프트웨어 만들기 강의노트1 (0) | 2019.09.15 |
자바스크립트 Blob 이란? (1) | 2019.08.13 |