Jest

공부의 기록·2022년 1월 23일
0

Node.JS

목록 보기
12/32
post-thumbnail

Introduce

본 문서는 2022년 1월 28일 에 작성되었습니다.

클린 애자일, 클린 코드를 읽고 한 달 이상의 시간이 흘렀습니다.
하지만 시간을 미루고 미뤄 1월이 되어서야 처음으로 테스트 코드라는 것을 작성 해보았습니다.


Installation

npm i -d jest

Package Script

편안하게 사용하기 위해서 다음의 스크립트를 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"

Options

  1. --experimental-vm-modules // es6+ syntac 사용 명시 (jest 를 위한 설정)
  2. --coverage // *.test.js 가 적용된 파일 중에 테스트 누락된 모듈을 알려주는 기능
  3. --clearCache // jest 의 cache를 지우는 옵션

Syntax

https://runebook.dev/ko/docs/jest/expect

describe(표기 문자열, 함수);

describe(표기 문자열, 함수);

표기 문자열은 콘솔 창에 표시 된다.
describe() 나 test() 등을 함수 영역 에 넣어서 집합으로 만들 수 있다.
테스트 결과를 비슷한 친구들끼리 모아서 보기 좋게 만드는 용도로 사용한다.

test(표기 문자열, 함수);

test(표기 문자열, 함수);

표기 문자열은 콘솔 창에 표시 된다.
테스르를 실행할 부분을 함수 에 넣어준다.
실제로 테스트를 할 부분을 적는다.

expect(값);

expect().비교대상 함수()

은 테스트 대상 함수의 결과물 을 의미한다.
비교대상 함수 는 toBe(값), toEqual(값), toBeFalsy() 등 수많은 함수들을 포함한다.
비교대상 함수 가 동일해야지 테스트를 통과 한 것이다.

실제로 테스트를 할 부분은 test 안에 적는다면,
해당 test 의 결과물이 예상한 대로 나왔는 지를 expect() 를 통해서 확인한다.


Example

해당 프로젝트는 Node, Exrpess, Jest 으로 진행되고 있습니다.
Github unchaptered / validator.test.js /유효성 검사 모듈 테스트 코드 작성
Github unchaptered / middleware.test.js / Express Middleware 테스트 코드 작성

✅ Validator.test.js

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
    });
});

# Id > Id Length

처음으로 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();
  });
});

# Id > Id Regex

이후 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 > Pw Compared

이후 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();
  });
});

# Password > Pw Length

이후 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();
  });
});

# Password > Pw Regex

이후 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.test.js

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
  })
});

# Middlewares > locals

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();
});

# Middlewares > preventLoginUser

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();
        });
    });

# Middlewares > preventLotoutUser

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("/");
  });
});

Error

profile
2022년 12월 9일 부터 노션 페이지에서 작성을 이어가고 있습니다.

0개의 댓글