[기본기 다지기] 단위테스트를 위한 expect 사용 연습(1)
아직 jest니 단위테스트니 어려운 것들 투성이다.
어느것에 대해 막힘없이 설명할 수 있다면 그것을 알고있다는 거라는데,
그런 의미에서 나는 아직 코딩에 대해 아는 것이 없을지도...
그래도 한발짝 밟다보면 어느순간 설명할 수 있을 정도로 늘어있지 않을까?
남들보다 나은 내가 아닌, 어제의 나보다 나아지기 위해 연습해보자... :(
아래는 내가 연습한 코드이다.
다른 블로그를 참고하여 이것저것 넣어서 결과값을 확인해보았다.
아, 사담이지만 jest는 프로젝트로 돌아가기 때문에 새 프로젝트를 검사하고 싶으면 거기다가 다시 설치해야 한다.
이전에 설치했으니까, 하고 안했다가 안돌아가서 왜 그런지 끙끙 댔었다.
아래 사이트에서 많은 정보를 얻을 수 있었는데, 이 jest 카테고리에 있는 3번 글을 참고하여 아래 코드를 실험해봤다.
코드에 대해 더 자세하게 알고 싶다면 아래 사이트를 보는 것을 추천한다.
그리고, 괜찮다면 해당 카테고리에 있는 1~3글을 모두 읽어보면 좋다.
참고한 사이트: https://www.js2uix.com/frontend/jest-study-step1/
Jest - 유닛 테스트 소개 및 설치 (1)
제가 프론트엔드 개발에서 처음으로 유닛 테스트를 경험했던게 Angular 를 처음 사용하던 시기였던것 같습니다. 그 당시엔 이건 뭐지? 왜 항상 Javascript 소스와 동일한 이름의 spec 파일이 생성될까?
www.js2uix.com
처음에 이렇게 양이 많아질줄 모르고 forTest 객체를 만들고 그때그때 필요에 따라 val을 만들었는데 나중에 가니 그게 의미가 없어졌다...
(실패)라고 적혀있지 않다면 해당 테스트는 통과되었다는 이야기이다.
const forTest = {
num: 1,
str: "str",
arr: [1,2,3,4],
set: new Set([1,2,3,4]),
map: {
child_1: "A",
child_2: "B",
},
null: null,
funCallback(callback, num){
if (num == this.num) callback() ;
},
random: Math.floor(Math.random()*10),
}
describe("test 기본_toBe()", () => {
let val = 1 ;
//expect().toBe(any) : 기본값 비교 또는 인스턴스의 참조 아이디 체크 용이
test(".toBe()_boolean", () => {
expect(forTest.num === val).toBe(true) ;
}) // forTest의 num은 val과 비교했을때 true이다.
test(".toBe()_map/str", () => {
expect(forTest.map.child_1).toBe("A") ;
}) // forTest의 map의 chile는 "A"이다.
test(".toBe()_num", () => {
expect(forTest.num).toBe(1) ;
}) // forTest의 num는 이다.
})
describe("test 기본_toEqual()", () => {
let val_1 = 1 ;
let val_2 = {
child_1: "A",
child_2: "B",
}
//expect().toEqual(value) : 기본값 비교 또는 인스턴스의 참조 아이디 체크 용이
test(".toEqual()_num", () => { // 동일한 내용 .toBe()도 통과
expect(forTest.num).toEqual(val_1) ;
}) // forTest의 num은 val_1과 내용이 같다. (통과)
test(".toEqual()_map", () => {
expect(forTest.map).toEqual(val_2) ;
}) // forTest의 map은 val_2과 내용이 같다. (통과)
test(".toEqual()_.not.toBe()", () => { // 구조가 동일한 것이지 같지 아니기에 true
expect(forTest.map).not.toBe(val_2) ;
}) // forTest의 map은 val_2과 동일하지않다. (통과)
test(".not.toEqual()", () => {
expect(forTest.str).not.toEqual("A") ;
}) // forTest의 str은 "A"과 같지않다. (통과)
})
describe("test 기본_toBeNull()", () => {
// .toBeNull() == .toBe(Null) 이지만, 오류메세지가 더 좋음
test(".toBeNull()_Null", () => {
expect(forTest.null).toBeNull() ;
}) // forTest.null == null
test(".toBeNull()_.toBe(Null)", () => {
expect(forTest.null).toBe(null) ;
}) // forTest.null == null
})
describe("test 기본_toBeTruthy()/toBeFalsy()", () => {
// boolean값 확인
let val = 2 ;
test(".toBeTruthy()", () => { // true = false를 제외한 모든
expect(forTest.num).toBeTruthy() ;
}) // forTest.num == true
test(".toBeFalsy()", () => { // false = [false, 0, "", null, undefined, NaN]
expect(forTest.null).toBeFalsy() ;
}) // forTest.null == false
test(".toBeTruthy()_val", () => {
expect(forTest.arr.includes(val)).toBeTruthy() ;
}) // forTest.arr는 val을 포함하고 있는 것은 true이다.
test(".toBeFalsy()_val", () => {
expect(forTest.num === val ).toBeFalsy() ;
}) // forTest.nun === val은 false이다.
})
describe("test 기본_toHaveLength()", () => {
// .toHaveLength(num): 길이를 잴 수 있고, 길이가 num과 같은지 비교
// expect(여기에 길이를 잴 수 없는 것이 들어가면 오류가 발생)
test(".toHaveLength()_str", () => {
expect(forTest.str).toHaveLength(3) ;
}) // forTest.str의 길이는 3
test(".toHaveLength()_arr", () => {
expect(forTest.arr).toHaveLength(4) ;
}) // forTest.arr의 길이는 4
test(".not.toHaveLength()", () => {
expect(forTest.str).not.toHaveLength(4) ;
}) // forTest.str의 길이는 4가 아니다.
test(".toHaveLength()_set", () => {
expect(forTest.set).not.toHaveLength(4) ;
}) // forTest.set의 길이는 4아다. (실패. set은 아예 사용불가)
})
describe("test 기본_toHaveProperty()", () => {
// expect(map).toHaveProperty(keypath, value?): map안에 key가 있는지,, value 생략 가능
// keypath는 ""처리해야함
test(".toHaveProperty()_key", () => {
expect(forTest).toHaveProperty("num") ;
}) // forTest 안에 num이란 key가 있다
test(".not.toHaveProperty()_key", () => {
expect(forTest).not.toHaveProperty("A") ;
}) // forTest 안에 "A"이란 key가 없다
test(".toHaveProperty()_key,value", () => {
expect(forTest).toHaveProperty("str", "str") ;
}) // forTest 안에 str이란 value를 가진 str이란 key가 있다
test(".not.toHaveProperty()_key,value", () => {
expect(forTest).not.toHaveProperty("str", "A") ;
}) // forTest 안에 "A"이란 value를 가진 str이란 key가 없다
test(".toHaveProperty()_map_1", () => { // test: toHaveProperty()_map_2 와 동일
expect(forTest.map).toHaveProperty("child_1", "A") ;
}) // forTest의 map에 "A"이란 value를 가진 child_1이란 key가 있다
test(".toHaveProperty()_map_2", () => { // test: toHaveProperty()_map_1 와 동일
expect(forTest).toHaveProperty("map.child_1", "A") ;
}) // forTest의 map에 "A"이란 value를 가진 child_1이란 key가 있다
})
describe("test 기본_toHaveBeenCalled()", () => { //아직 미숙
// .toHaveBeenCalled() : 모의함수가 실행되었는지 확인할 수 있음
// .toBeCalled 로도 사용 가능
// 바깥에다가(여기다가) 콜백함수를 만들면 전체로 콜백이 쌓여서 뒤에 not.이 안돌아감, 테스트에 오류가 생김
let val_1 = 1 ;
let val_2 = 2 ;
test(".toHaveBeenCalled()", () => {
let callback = jest.fn() //jest.fn() 추후에 다룰
forTest.funCallback(callback, val_1) ;
expect(callback).toHaveBeenCalled() ;
}) // forTest.funCallback을 실행했을 때 callback 함수가 실행됨
test(".toBeCalled()", () => {
let callback = jest.fn() //jest.fn() 추후에 다룰
forTest.funCallback(callback, val_1) ;
expect(callback).toBeCalled() ;
}) // test: .toHaveBeenCalled()와 동일
test(".not.toHaveBeenCalled()", () => {
let callback = jest.fn() //jest.fn() 추후에 다룰
forTest.funCallback(callback, val_2) ;
expect(callback).not.toHaveBeenCalled() ;
}) // forTest.funCallback을 실행했을 때 callback 함수가 실행되지 않음
test(".not.toBeCalled()", () => {
let callback = jest.fn() //jest.fn() 추후에 다룰
forTest.funCallback(callback, val_2) ;
expect(callback).not.toBeCalled() ;
}) // test: .toHaveBeenCalled()와 동일
})
describe("test 기본_expect.objectContaining()", () => { //아직 미숙
// expect.objectContaining(object) : object의 요소들을 포함하고 있는지
// toEqual / toBeCalledWith 안에서 사용
// .toBeCalledWith(parameter): 해당 파라미터를 가진 함수가 한번이라도 호출된 적이 있으면 통과하는 함수이다.
// .expect.any(constructor): 주어진 생성자와 값이 해당 생성자에 일치하는 지를 테스트
const val_1 = { a: 1, b: 2 } ;
test ("expect.objectContaining()_toEqual_1", () => {
expect( { a: 1, b: 2, c: 3 } ).toEqual(expect.objectContaining(val_1))
}) // { a: 1, b: 2, c: 3 } 안에 val_1의 요소가 모두 포함되어 있다.
test ("expect.objectContaining()_toEqual_2", () => {
expect( { a: 1, b: 3, c: 3 } ).toEqual(expect.objectContaining(val_1))
}) // { a: 1, b: 3, c: 3 } b의 value가 일치하지 않음 (실패)
test ("expect.objectContaining()_toEqual_not", () => {
expect( { a: 1, b: 3, c: 3 } ).not.toEqual(expect.objectContaining(val_1))
}) // { a: 1, b: 3, c: 3 } 안에 val_1의 모든 요소가 일치하지는 않음 (통과)
test ("expect.objectContaining()_toEqual_3", () => {
expect( { a: 1, b: 2, c: 3 } ).toEqual(expect.objectContaining( { a: expect.any(Number)} ))
}) // expect.any를 여기에 사용해도 괜찮음 (통과)
const funA = (callback) => {
callback({ a:1, b:"2", c: 3 }) ;
}
test("expect.objectContaining()_toBecalledWith_1", () => {
const callback = jest.fn() ;
funA(callback) ;
expect(callback).toBeCalledWith(
expect.objectContaining({
a: 1, // expect.any(Number) 과 동일한 결과, expect.any(1)라고 쓰면 안됨
b: expect.any(String),
c: expect.any(Number)
})
)
})
})
describe("test 기본_expect.arrayContaining()", () => {
// expect.arrayContaining(array) : array의 요소들을 포함하고 있는지
// toEqual / toBeCalledWith 안에서 사용
// .toBeCalledWith(parameter): 해당 파라미터를 가진 함수가 한번이라도 호출된 적이 있으면 통과하는 함수이다.
// .expect.any(constructor): 주어진 생성자와 값이 해당 생성자에 일치하는 지를 테스트
const valArr = [ 1, 2 ] ;
test ("expect.arrayContaining()_toEqual", () => {
expect( [ 1, "A", 2, "B" ] ).toEqual(expect.arrayContaining(valArr))
}) // [ 1, "A", 2, "B" ] 안에 valArr의 요소가 모두 포함되어 있다.
test ("expect.arrayContaining()_toEqual_not", () => {
expect( [ 1, "A" ] ).not.toEqual(expect.arrayContaining(valArr))
}) // [ 1, "A" ] 안에 valArr의 모든 요소가 포함되어있지는 않다.
const funA = (callback) => {
callback( [ 1, 2, 3, 4 ] ) ;
}
test("expect.arrayContaining()_toBecalledWith_1", () => {
const callback = jest.fn() ;
funA( callback ) ;
expect( callback ).toBeCalledWith(expect.arrayContaining( [ 1, 2 ] ))
})
})
describe("test 기본_expect.stringMatching()", () => {
// expect.stringMatching(string|regexp) : 예상 문자열이나 정규표현식(regexp)과 일치하는지
// toEqual / toBeCalledWith 안에서 사용
test("expect.stringMatching()_map_1", () => {
expect({ a: "abcd"}).toEqual({ a : expect.stringMatching(/^ab/)})
}) // { a: "abcd" }는 "ab"라는 string이 포함된 value를 가진, key가 a인 객체이다.
test("expect.stringMatching()_map_2", () => {
expect({ a: "abcd"}).toEqual({ a : expect.stringMatching("ab")})
}) // { a: "abcd" }는 "ab"라는 string이 포함된 value를 가진, key가 a인 객체이다.
// expect.arrayContaining()와 expect.objectContaining()와 함께 사용할수도 있음
test("expect.stringMatching()_map_3", () => {
expect({ a: "abcd", b: 1 }).toEqual(expect.objectContaining({ a : expect.stringMatching(/^ab/)}))
}) // { a: "abcd" } 안에 "ab"라는 string이 포함된 value를 가진, key가 a인 객체가 포함되어 있다.
test("expect.stringMatching()_arr_1", () => {
expect( [ "ab", "bb" ] ).toEqual([expect.stringMatching("a"), expect.stringMatching("b")])
}) // [ "ab", "bb" ] 는 ["a"가 포함된 string, "b"가 포함된 string]과 같다. (index를 지켜야 함)
test("expect.stringMatching()_arr_2", () => {
expect( [ "ab", "bb" ] ).toEqual([expect.stringMatching("b"), expect.stringMatching("a")])
}) // index순서가 맞지 않기 때문에 오류로 뜸
test("expect.stringMatching()_arr_3", () => {
expect( [ "ab" ] ).not.toEqual([expect.stringMatching("c")])
}) // [ "ab"] 는 ["c"가 포함된 string]이 있지 않다. (index를 지켜야 함)
})