middlewares.js에 있는 isLoggedIn과 isNotLoggedIn 함수를 테스트해보겠습니다.
isLoggedIn 함수와 isNotLoggedIn 함수를 불러와 네 개의 테스트를 작성했습니다.
아직 내용은 입력하지 않았습니다. describe 함수는 처음 보는 것일텐데요.
이 함수는 테스트를 그룹화해주는 역할을 합니다.
test 함수와 마찬가지로 첫 번째 인수는 그룹에 대한 설명이고, 두 번째 인수인 함수는 그룹에 대한 내용입니다.
테스트 내용을 작성하기에 앞서 middlewares.is를 다시 보고 오겠습니다
실제 코드에서는 익스프레스가 req, res 객체와 next 함수를 인수로 넣었기에 사용할 수 있었지만,
테스트 환경에서는 어떻게 넣어야 할지 고민됩니다.
req 객체에는 isAuthenticated 메서드가 존재하고 res 객체에도 status, send, redirect 메서드가 존재하는데,
코드가 성공적으로 실행되게 하려면 이것들을 모두 구현해야 합니다.
이럴 때는 과감하게 가짜 객체와 함수를 만들어 넣으면 됩니다.
테스트의 역할은 코드나 함수가 제대로 실행되는지를 검사하고 값이 일치하는지를 검사하는 것이므로,
테스트 코드의 객체가 실제 익스프레스 객체가 아니어도 됩니다.
이렇게 가짜 객체, 가짜 함수를 넣는 행위를 모킹(mocking)이라고 합니다.
req, res, next를 모킹했습니다. 함수를 모킹할 때는 jest.fn 메서드를 사용합니다.
함수의 반환값을 지정하고 싶다면 jest.fn(() => 반환값)을 사용하면 됩니다.
isAuthenticated는 로그인 여부를 알려주는 함수이므로 테스트 내용에 따라 true 또는 false를 반환하고,
res.status는 res.status(403).send('hello')처럼 메서드 체이닝이 가능해야 하므로 res를 반환하고 있습니다.
실제로는 req, res 객체에 많은 속성과 메서드가 들어있겠지만,
지금 테스트에서는 isAuthenticated나 status, send만 사용하면 됩니다.
실제 테스트가 실행되기 전에만 모킹한 객체를 선언하면 됩니다.
res객체는 describe함수 안에 선언했는데, 왜 req객체는 test 함수 안에 선언했는지 궁금할 수 있습니다.
res 객체는 여러 테스트에서도 사용하는 모양이 같으므로 여러 테스트에서 재활용이 가능하지만,
req 객체는 isAuthenticated 메서드가 다른 모양으므로 각각의 test에 따로 선언했습니다.
test 함수 내부에서는 모킹된 객체와 함수를 사용해 isLoggedIn 미들웨어를 호출한 후
expect로 원하는 내용대로 실행되었는지 체크하면 됩니다.
toBeCalledTimes(숫자)는 정확하게 몇 번 호출되었는지를 체크하는 메서드고,
toBeCalledWith(인수)는 특정 인수와 함께 호출되었는지를 체크하는 메서드입니다. 테스트를 돌려보면 모두 통과합니다.
이렇게 작은 단위의 함수나 모듈이 의도된 대로 정확히 작동하는지 테스트하는 것을
유닛 테스트(unit test) 또는 단위 테스트라고 부릅니다.
나중에 함수를 수정하면 기존에 작성한 테스트는 실패하게 됩니다.
따라서 함수가 수정되었을 때 어떤 부분이 고장나는지를 테스트를 통해 알 수 있습니다.
테스트 코드도 기존 코드가 변경된 것에 맞춰서 수정해야 합니다.
uesr 컨트롤러 테스트도 해보겠습니다.
contollers/user.js 파일을 다시 한번 보겠습니다.
여기서 follow 함수를 테스트 해봅시다.
controllers/user.test.js를 작성합니다.
follow 함수는 async 함수 이므로 await을 붙여야 함수가 전부 실행 완료가 된 후 expexct함수가 실행됩니다.
하지만 이 테스트는 실패합니다.
그 이유는 follow 컨트롤러 안에는 user라는 모델이 들어있습니다.
이 코델은 실제 데이터베이스와 연결되어 잇으므로 테스트 환경에서는 사용할 수 없습니다.
따라서 user모델도 모킹해야 합니다.
jset에서는 모듈도 모킹할 수 있습니다. jest.mock 메서드를 사용합니다.
jest.mock 메서드에 모킹할 모듈의 경로를 인수로 넣고, 그 모듈을 불러옵니다.
이러면 해당 모듈(user)의 메서드는 전부 가짜 메서드가 됩니다.
예를 들어 user.finone등의 가짜 메서드가 됩니다.
가짜 메서드에는 mockReturnValue 등의 메서드가 생성됩니다.
따라서 User.findOne.mockReturnValue메서드로 User.findone의 가짜 반환값을 지정할 수 있습니다.
첫 번째 테스트에서는 mockReturnValue 메서드를 통해 User.findOne이 { addFollowing() } 객체를 반환 하도록 했습니다.
이는 DB로 부터 사용자를 찾은 후 팔로잉을 추가하는 상황을 테스트하기 위함입니다.
user.js를 보면
user 변수가 { addFollowing() } 객체로 모킹되므로 await user.addFollowing 을 호출할 수 있습니다.
모킹 할 때는 모킹된 결과물이 실제 코드에서 어떤 역활을 할지 미리 예상하는 것이 중요합니다.
두번쨰 테스트에서는 User.jindOne이 null을 반환해 사용자를 찾지 못한 상황을 테스트합니다.
세번쨰 테스트에서는 Promise.reject로 에러가 발생하도록 했습니다.
DB연결에 에러가 발생한 상황을 모킹힌것입니다.
실제코드에서는 catch 문으로 이동하게 됩니다.
실제 데이터베이스에 팔로잉을 등록하는것이 아니므로 제대로 테스트 되는 것인지 걱정할 수도 있습니다.
이처럼 테스트를 해도 실제 서비스의 실제 데이터베이스에서는 문제가 발생할 수 있습니다.
그럴때는 유닛 테스트말고 다른 종류의 테스트를 진행해야 합니다.
이를 점검하기 위해 통합테스트나 시스템 테스트를 하곤 합니다.
Note
매번 테스트 할때마다 req,res,nest를 모킹하는것에 불만이 있을수도 있습니다.
중요한 로직이 아닌 익스프레스 객체를 모킹하고 있는것이 무의미하다고 느낄 수도 있습니다.
그래서 실제로 컨트롤러에서 서비스라는 계층을 분리하기도 합니다ㅏ.
서비스는 익스프레스의 req,res,next에 관해 알지 못합니다.
반대로 컨트롤러는 User와 같은 모델에 대해 알지 못합니다.
이와같은 원칙으로 분리하면 됩니다.
그러면 컨트롤러 테스트에서는 모델을 모킹할 필요가 없어지고 서비스테스트에서는
req,res,next를 모킹할 필요가 없어 테스트가 좀더 쉬워집니다.
'clone toy projects > node_express_sns' 카테고리의 다른 글
통합 테스트 (0) | 2023.08.29 |
---|---|
테스트 커버리지 (0) | 2023.08.27 |
테스트 준비하기 (0) | 2023.08.25 |
CORS 이해하기 (0) | 2023.08.25 |
사용량 제한 구현하기 (0) | 2023.08.25 |