본 문서는 2022년 1월 28일 에 작성되었습니다.
클린 애자일, 클린 코드를 읽고 한 달 이상의 시간이 흘렀습니다.
하지만 시간을 미루고 미뤄 1월이 되어서야 처음으로 테스트 코드라는 것을 작성 해보았습니다.
npm i -d jest
편안하게 사용하기 위해서 다음의 스크립트를 package.json 에 추가해줍시다.
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
"test:clear": "node --experimental-vm-modules node_modules/jest/bin/jest.js --clearCache"
describe(표기 문자열, 함수);
표기 문자열은 콘솔 창에 표시 된다.
describe() 나 test() 등을 함수 영역 에 넣어서 집합으로 만들 수 있다.
테스트 결과를 비슷한 친구들끼리 모아서 보기 좋게 만드는 용도로 사용한다.
test(표기 문자열, 함수);
표기 문자열은 콘솔 창에 표시 된다.
테스르를 실행할 부분을 함수 에 넣어준다.
실제로 테스트를 할 부분을 적는다.
expect(값).비교대상 함수()
값은 테스트 대상 함수의 결과물 을 의미한다.
비교대상 함수 는 toBe(값), toEqual(값), toBeFalsy() 등 수많은 함수들을 포함한다.
값 과 비교대상 함수 가 동일해야지 테스트를 통과 한 것이다.
실제로 테스트를 할 부분은 test 안에 적는다면,
해당 test 의 결과물이 예상한 대로 나왔는 지를 expect() 를 통해서 확인한다.
해당 프로젝트는 Node, Exrpess, Jest 으로 진행되고 있습니다.
Github unchaptered / validator.test.js /유효성 검사 모듈 테스트 코드 작성
Github unchaptered / middleware.test.js / Express Middleware 테스트 코드 작성
Validaotr.js 는 유효성 검사 모듈이 모여있는 파일입니다.
export function idLength (id) { return Boolean }
export function idRegex (id) { return Boolean }
export function passwordCompared(password,password2) { return Boolean }
export function passwordLength(password) { return Boolean }
export function passwordRegex(password) { return Boolean }
위와 같이 ID 검사 모듈 2종, PW 검사 모듈 3종을 포함하고 있습니다.
따라서 다음과 같은 구조로 Validator.test.js 를 작성해보았습니다.
describe("Validators", ()=>{
describe("Id Validators", ()=>{
// Id Module Test
});
describe("Password Validators", ()=>{
// Password Module Test
});
});
처음으로 idLength 모듈의 테스트 케이스 3종을 작성했습니다.
describe 를 통해서 idLength 모듈 내부의 조건식을 명시하고
이후, test 를 통해서 세 개의 테스트 케이스를 실행하였습니다.
각각의 테스트 케이스에 매개변수로 길이가 다른 문자열을 입력시키고
이에 따라서 테스트 기댓값 false, false, true 을 확인하는 것으로 테스트 코드 작성이 완료되었습니다.
describe("Id Length 6 < * <=30", ()=>{
test(`id.length = 3`, ()=>{
const userId="tmp"
const result=idLength(userId);
expect(result).toBeFalsy();
});
test(`id.length = 21`, ()=>{
const userId="abcdeabcdeabcdeabcdea";
const result=idLength(userId);
expect(result).toBeFalsy();
});
test(`id.length = 10`,()=>{
const userId="bmgktlqejr";
const result=idLength(userId);
expect(result).toBeTruthy();
});
});
이후 idRegex 모듈의 테스트 케이스 3 X 5 종을 작성하였습니다.
describe 를 통해서 idRegex 모듈 내부의 정규표현식 을 명시하고
이후, describe 로 한글, 자음, 모음 포함 의 테스트 그룹 명시하였습니다.
이후,
"안abcd", "a안bcd", "ab안cd", "abc안d", "abcd안"
"ㅇabcd", "aㅇbcd", "abㅇcd", "abcㅇd", "abcdㅇ"
"ㅏabcd", "aㅏbcd", "abㅏcd", "abcㅏd", "abcdㅏ"
와 같은 테스트 케이스를 작성하고 모듈에 매개변수로 입력시켰습니다.
이에 따라서 테스트 기댓값 false x 15 를 확인하는 것으로 테스트 코드 작성이 완료되었습니다.
describe("Id Regex /[ㄱ-ㅎㅏ-ㅣ가-힣]/g", ()=>{
describe("한글 포함 테스트", ()=>{
test("id = 안abcd", ()=>{
const userId="안abcd";
const result=idRegex(userId);
expect(result).toBeFalsy();
});
test("id = a안bcd", ()=>{
const userId="a안bcd";
const result=idRegex(userId);
expect(result).toBeFalsy();
});
test("id = ab안cd", ()=>{
const userId="ab안cd";
const result=idRegex(userId);
expect(result).toBeFalsy();
});
test("id = abc안d", ()=>{
const userId="abc안d";
const result=idRegex(userId);
expect(result).toBeFalsy();
});
test("id = abcd안", ()=>{
const userId="abcd안";
const result=idRegex(userId);
expect(result).toBe(false);
});
})
이후 Password Validator 에 두 비밀번호를 비교하는 코드를 작성하였습니다.
두 비밀번호가 같은 경우와 다른 경우를 확인해보았습니다.
describe("Pw Compared a === b", ()=>{
let password="";
let password2="";
test("rkawk13@! === rkawk13@!", ()=>{
password="rkawk13@!";
password2="rkawk13@!";
const result=passwordCompared(password,password2);
expect(result).toBeTruthy();
});
test("rkawk13@! === rkawk14@!", ()=>{
password="rkawk13@!";
password2="rkawk14@!";
const result=passwordCompared(password,password2);
expect(result).toBeFalsy();
});
});
이후 passwordLength 모듈의 테스트 케이스 3종을 작성하였습니다.
describe 를 통해서 passwordLength 모듈 내부의 조건식을 명시하고
이후, test 를 통해서 세 개의 테스트 케이스를 실행하였습니다.
각각의 테스트 케이스에 매개변수로 길이가 다른 문자열을 입력시키고
이에 따라서 테스트 기댓값 false, false, true 를 확인하는 것으로 테스트 코드 작성이 완료 되었습니다.
describe("Pw Length 6 < * <=20", ()=>{
let password="";
test("passowrd.length = 3", ()=>{
password="tmp";
const result=passwordLength(password);
expect(result).toBeFalsy();
});
test("passowrd.length = 21", ()=>{
password="abcdeabcdeabcdeabcdea";
const result=passwordLength(password);
expect(result).toBeFalsy();
});
test("passowrd.length = 8", ()=>{
password="dafmdslkaf";
const result=passwordLength(password);
expect(result).toBeTruthy();
});
});
이후 passwordRegex 모듈의 테스트 케이스 5종을 작성하였습니다.
descirbe 를 통해서 passwordRegex 모듈 내부의 정규표현식을 명시하고
이후,
"abcd","@abcd","ab@cd","abc@d","abcd@"
와 같은 테스트 케이스를 작성하고 모듈에 매개변수로 입력시켰습니다.
이에 따라서 테스트 기댓값 true, false, false, false ,false 를 확인하는 것으로 테스트 코드 작성이 완료되었습니다.
describe("Pw Regex /\W{1,}/", ()=>{
let password="";
test("pw = abcd", ()=>{
password="abcd";
const result=passwordRegex(password);
expect(result).toBeFalsy();
});
test("pw = @abcd", ()=>{
password="@abcd";
const result=passwordRegex(password);
expect(result).toBeTruthy();
});
test("pw = ab@cd", ()=>{
password="ab@cd";
const result=passwordRegex(password);
expect(result).toBeTruthy();
});
test("pw = abc@d", ()=>{
password="abc@d";
const result=passwordRegex(password);
expect(result).toBeTruthy();
});
test("pw = abcd@", ()=>{
password="abcd@";
const result=passwordRegex(password);
expect(result).toBeTruthy();
});
})
middleware.js 는 router - controller 사이에서 반복적으로 검사하는 항목들을 모듈화 시킨 구조입니다.
validators.js 와 다른 점은,
middleware.js 는 주로 로컬, 세션 등을 통한 사용자 정보 확인 및 대조 나 multer 와 같은 업로드 모듈 사용 시의 공용 설정값 제어 등을 위해서 사용한다는 점입니다.
export const localMiddleWare=(req,res,next)=>{
if (세션이 있다면) {
로컬 스토리지에 옮겨담고 다음 함수(next())를 실행
}
로컬 스토리지를 비우고 다음 함수(next())를 실행
}
export const preventLoginUser=(req,res,next)=>{
if (세션에 loggedIn 이 있다면) {
상태 코드 304 를 던지고 홈 페이지(redirect("/"))로 이동
} else {
다음 함수(next())를 실행
}
}
export const preventLogoutUser=(req,res,next)=>{
if (세션에 loggedIn 이 없다면) {
상태 코드 304 를 던지고 홈 페이지(redirect("/"))로 이동
} else {
다음 함수(next())를 실행
}
}
위와 같이 총 3개의 미들웨어를 가지고 있습니다.
따라서 다음과 같은 구조로 middleware.test.js 를 작성해보았습니다.
describe("Middlewares", ()=>{
describe("localMiddleware", ()=>{
// localMiddleWare Module Test
})
describe("preventLoginUser", ()=>{
// preventLoginUser Module Test
})
describe("prventLogoutUser", ()=>{
// preventLogoutUser Module Test
})
});
middleware 테스트를 위해서는 가짜 함수, mockup 이 필요합니다.
jest.fn() 등 의 jest 내부에서 가짜 함수를 만들어줍니다.
하지만 이를 적절하게 사용하기 힘들다면 텅빈 함수를 만들어서 사용해도 됩니다.
localsMiddleWare 는 2가지 분기점을 가지며 내부에서 2개의 데이터를 변경합니다.
따라서 2개의 test() 를 만들고 내부에서 2개의 expect() 를 진행해야 합니다.
const req={
session: {
loggedIn: true,
user: {
nickname: "testerNickname",
userid: "testerUserid",
userpw: "testerPassword12@"
}
}
};
const res={
locals: {
loggedIn: false,
loggedInUser: null
}
};
function success() {
return true;
};
test("login sinario", ()=>{
localMiddleWare(req,res,success);
expect(res.locals.loggedIn).toBeTruthy();
expect(res.locals.loggedInUser).toMatchObject(req.session.user);
});
test("logout sinario", ()=>{
// req.session.destroy() 를 하면 undefined 값을 가진다.
req.session=undefined;
localMiddleWare(req,res,success);
expect(res.locals.loggedIn).toBeFalsy();
expect(res.locals.loggedInUser).toBeNull();
});
preventLoginUser 도 위와 같습니다.
describe("preventLoginUser", ()=>{
const req={
session: {
loggedIn: true,
user: {
nickname: "testerNickname",
userid: "testerUserid",
userpw: "testerPassword12@"
}
}
};
const res={
locals: {
loggedIn: false,
loggedInUser: null
},
statusCode: null,
URL: null,
status: function status(statusCode) {
res.statusCode=statusCode;
return res;
},
redirect: function redirect(URL) {
res.URL=URL;
return res;
}
};
const next=jest.fn();
test("loginUser sinario", ()=>{
preventLoginUser(req,res,next);
expect(res.statusCode).toBe(304);
expect(res.URL).toBe("/");
});
test("logoutUser sinario", ()=>{
res.statusCode=null;
res.URL=null;
/* req.session.destory() 를 하면 undefined 값을 가진다.
*/
req.session.loggedIn=null;
req.session.user=null;
preventLoginUser(req,res,next);
expect(res.statusCode).toBeNull();
expect(res.URL).toBeNull();
});
});
preventLogoutUser 도 위와 같습니다.
describe("prventLogoutUser", ()=>{
const req={
session: {
loggedIn: true,
user: {
nickname: "testerNickname",
userid: "testerUserid",
userpw: "testerPassword12@"
}
}
};
const res={
locals: {
loggedIn: false,
loggedInUser: null
},
statusCode: null,
URL: null,
status: function status(statusCode) {
res.statusCode=statusCode;
return res;
},
redirect: function redirect(URL) {
res.URL=URL;
return res;
}
};
const next=jest.fn();
test("loginUser sinario", ()=>{
preventLogoutUser(req,res,next);
expect(res.statusCode).toBeNull();
expect(res.URL).toBeNull();
});
test("logoutUser sinario", ()=>{
/* req.session.destory() 를 하면 undefined 값을 가진다.
*/
req.session.loggedIn=null;
req.session.user=null;
preventLogoutUser(req,res,next);
expect(res.statusCode).toBe(304);
expect(res.URL).toBe("/");
});
});