jest spyOn

jathazp·2022년 3월 13일
0

jest.spyOn()

mocking에는 스파이(spy)라는 개념이 있다. 현실이나 영화 속에서 스파이라는 직업은 “몰래” 정보를 캐내야 한다. 테스트를 작성할 때도 이처럼, 어떤 객체에 속한 함수의 구현을 가짜로 대체하지 않고, 해당 함수의 호출 여부와 어떻게 호출되었는지만을 알아내야 할 때가 있다. 이럴 때, Jest에서 제공하는 jest.spyOn(object, methodName) 함수를 이용하면 된다.

const calculator = {
  add: (a, b) => a + b,
};

const spyFn = jest.spyOn(calculator, "add");

const result = calculator.add(2, 3);

expect(spyFn).toBeCalledTimes(1);
expect(spyFn).toBeCalledWith(2, 3);
expect(result).toBe(5);

위 예제를 보면, jest.spyOn() 함수를 이용해서 calculator 객체의 add라는 함수에 스파이를 붙였다. 따라서 add 함수를 호출 후에 호출 횟수와 어떤 인자가 넘어갔는지 감증할 수 있다. 하지만 가짜 함수로 대체한 것은 아니기 때문에 결과 값은 원래 구현대로 2와 3의 합인 5가 되는 것을 알 수 있다.

https://www.daleseo.com/jest-fn-spy-on/

Jest Date Mocking

Date

Date.now = jest.fn(() => +new Date('2022-03-10'));

moment

//방법1
//moment는 결국 내부적으로 Date.now를  호출한다
//결국 Date.now를 모킹하면 됨
//test 안에 선언
Date.now = jest.fn(() => new Date("2022-03-15T12:33:37.000Z"));

//방법2
//밖에서 선언
jest.mock('moment', () => {
  return () => jest.requireActual('moment')('2020-01-01T00:00:00.000Z');
});

https://stackoverflow.com/questions/29719631/how-do-i-set-a-mock-date-in-jest

+)
new Date(날짜)

에서 날짜가 2022-03-10 00:00:00 의 형태면 그냥
대한민국시 00시00분00초로 인식을하고(UTC 03-09 15:00)

날짜가 2022-03-10T00:00:00.000Z 라면 UTC 시로 인식한다.

그러니까 T-Z 를 따로 넣어주지 않는다면 일반적으로 대한민국 시로 인식을 하는듯 하다

추가로 시간을 적지 않아도 UTC로 인식이 된다.
아래 결과 참고

const fir = new Date('2022-03-05');
const sec = new Date('2022-03-05 00:00:00');

console.log(fir);
console.log(sec);

//출력
fir : 2022-03-05 T00:00:00Z
sec : 2022-03-04 T15:00:00Z

new Date Mocking

jest.useFakeTimers('modern');
jest.setSystemTime(new Date(2022, 2, 10));
jest.useRealTimers();



연쇄적으로 호출되는 함수에 대한 모킹 방법

    Challenge.findById.mockImplementationOnce(() => ({
        lean: jest.fn().mockReturnValue({ _id: 1 }),
    }));

이제부터 연쇄 모킹할때는 mockImplementationOnce 를 이용하자.

그전에 User.find = jest.fn(()=>({sort: ()=>({limit:jest.fn()}) }))
이런식으로 했을때는 모킹을 두번해야하는 불편함이 있었는데 이런식으로 mockImplementation을 하면 함수를 한번 실행시키므로 바로 연쇄적인 모킹을 가능하게 한다.

ref) Error 발생시킬때도 mockImplementation을 쓰자.

Random 함수 모킹

beforeEach(() => {
    jest.spyOn(global.Math, 'random').mockReturnValue(0.123456789);
});

afterEach(() => {
    jest.spyOn(global.Math, 'random').mockRestore();
})

Throw Error 모킹

yourMockInstance.mockImplementation(() => {
  throw new Error();
});

node-mocks-http 모듈

아이고 !!! req 와 res 객체를 일일히 모킹해줄 필요가 없었다!!!!!!!
찾아보지 않은 내가 바보다.
jest 강의에서 코드를 뜯어가며 에러가 나지 않게 req,res 객체를 직접 만들어주는 모습을 보고 다른 방법을 찾아보지 않았는데

node-mocks-http 모듈을 사용하면 req,res 객체를 쉽게 모킹할 수 있다.

beforeeach로 매 test마다 req,res 객체를 모킹시키고,
req.body = ~ 처럼 값을 쉽게 세팅할 수 있다.

const notUser = require('../data/notUser.json');
const notID = require('../data/notID.json');
const isID = require('../data/isID.json');
const findOneData = require('../data/userFindOne.json');
const updateOneData = require('../data/userUpdateOne.json');

beforeEach(() => {
    req = httpMocks.createRequest();
    res = httpMocks.createResponse();
    next = null;
});

describe('node-mailer를 통한 ID/PW 찾기', () => {
    test('이메일 입력값이 없으면 입력하라는 에러 발생!', async () => {
        await emailController.sendEmail(req, res, next);
        expect(res._getJSONData()).toStrictEqual({
            fail: '이메일을 입력해주세요.',
        });
    });
    test('유저 정보가 없으면 에러 발생', async () => {
        req.body.email = notUser;
        User.findOne.mockImplementation(() => {
            throw new Error();
        });
        await emailController.sendEmail(req, res, next);
        expect(res._getJSONData()).toStrictEqual({
            fail: '다시 입력해주세요.',
        });
    });
    test('ID 입력 없는 경우 ID 찾아줌', async () => {
        req.body = notID;
        User.findOne.mockReturnValue(findOneData);
        await emailController.sendEmail(req, res, next);
        expect(res._getJSONData()).toStrictEqual({
            success: '아이디가 메일로 전송되었습니다.',
        });
    });
    test('ID 입력 있는 경우 PW 찾아줌', async () => {
        req.body = isID;
        User.findOne.mockReturnValue(findOneData);
        User.updateOne.mockReturnValue(updateOneData);
        await emailController.sendEmail(req, res, next);
        expect(res._getJSONData()).toStrictEqual({
            success: '임시 비밀번호가 메일로 전송되었습니다.',
        });
    });
});

내 코드는 아니다.

데이터 파일도 .json으로 만들어 따로 관리해주면 깔끔하게 재활용도 가능하다. 첫 테스트 코드 작성이다보니 너무 비효율적으로 짜게 되었다. 다음에 짜면 더 깔끔하게 잘 구성할 수 있을것 같다.

0개의 댓글