const mockQuestions = (answers) => {
MissionUtils.Console.readLine = jest.fn();
answers.reduce((acc, input) => {
return acc.mockImplementationOnce((question, callback) => {
callback(input);
});
}, MissionUtils.Console.readLine);
};
위 함수는 우테코 5기 프리코스 3주차 미션의 테스트 코드에 있는 함수이다. 하나하나 분석해보자.
MissionUtils.Console.readLine = jest.fn();
함수 내부 첫 번째 줄만 봐도 벌써 알아야 할 것이 많다. 일단 readLine
이 하는 역할 부터 알아보자.
우아한테크코스 5기 프리코스의 채점은 npm i
를 통해 모듈을 다운로드 받고, npm test
를 통해 jest
라이브러리의 테스트 코드를 모두 통과하는지 확인하는 방식으로 진행된다. 이때 다운로드 되는 모듈 중에는
이 친구가 포함돼있다. 미션을 채점하기 위해 우테코에서 직접 모듈을 만든 것으로 보인다. 이 중 readLine
을 포함한 console.js
를 보자.
import readline from "readline";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
class Console {
constructor() {}
static readLine(query, callback) {
if (arguments.length !== 2) {
throw new Error("arguments must be 2.");
}
if (typeof query !== "string") {
throw new Error("query must be string");
}
if (typeof callback !== "function") {
throw new Error("callback must be function");
}
if (callback.length !== 1) {
throw new Error("callback must have 1 argument");
}
rl.question(query, callback);
}
}
console.js
중 readLine
과 관련된 친구들만 발췌해왔다.
코드의 가장 윗 부분을 보면 readline
을 import해오고, createInterface
메서드를 사용해 무언갈 생성하여 r1
에 대입함을 확인할 수 있다.
node.js 공식 문서를 확인해 본 결과 createInterface
는 새로운 readline.Interface
인스턴스를 생성함을 알 수 있었다. 더 다룰 내용이 남았지만 잠시 넘어가겠다.
뒤이어 나오는 Console
클래스를 보자.
발췌한 코드 속 Console
클래스는 readLine
이라는 static 메서드를 가지고 있다. readLine
메서드는 query
와 callback
이라는 두 가지 인자값을 넘겨받는다.
인자값을 넘겨받은 readLine
메서드는 여러 예외 상황을 처리하고, 예외 상황에 걸리지 않는다면
r1.question(query, callback);
을 호출한다. 여기서 r1
은 아까 언급했던 readline
의 createInterface
메서드를 통해 생성된 readline.Interface
이다. 이렇게 생성된 인스턴스의 question
메서드를 호출하는 것이다.
node.js 공식문서에서 찾아본 결과 question
메서드의 역할은 다음과 같다.
query
를 출력한다. (질문 역할)- 사용자에게
query
에 해당하는 응답을 입력받는다.
question
메서드가 호출되면 입력이 발생하기 전까지 프로그램은 잠시 대기한다.- 입력이 발생하면
callback
의 첫번째 인자로 이를 전달한다.
따라서 이를 종합해본 결과 MissionUtils.Console.readLine
은 사용자의 입력값을 처리하는 함수임을 알 수 있다.
혹시 까먹었을 수 있겠으나 우리의 목적은
MissionUtils.Console.readLine = jest.fn();
이 코드가 어떤 친구인지 분석하는 것이었다.
다시 본론으로 돌아와서 뒷 부분을 마저 분석해보자. 이번엔 jest.fn()
이 어떤 역할을 하는지 알아볼 차례이다.
jest 공식 문서를 찾아본 결과 jest.fn()
은 새로운, 사용되지 않은 mock function을 반환한다고 쓰여있었다.
또, mock function은 일종의 스파이 역할을 하는 함수라고 한다. 함수의 반환값을 통해 테스트를 하는 것이 아닌, 함수의 동작 과정을 테스트 하고 싶을 때 사용하는 친구이다.
이제 남은 뒷 부분을 살펴보자.
answers.reduce((acc, input) => {
return acc.mockImplementationOnce((question, callback) => {
callback(input);
});
}, MissionUtils.Console.readLine);
mdn web docs에서 찾아본 결과 reduce
함수는 위와 같은 인자값을 갖는다고 한다. 다시 우리가 볼 코드로 돌아와서 생각해보자.
우리가 가진 코드에서 reduce에 전달된 녀석은 callback
과 initialValue
이다. 또, callback
에서 쓰인 인자는 accumulator
와 currentValue
이렇게 두 가지이다. 우리의 코드를 보면
(acc, input) => {
return acc.mockImplementationOnce((question, callback) => {
callback(input);
});
};
이 친구가 callback
이고, acc
가 accumulator
, input
이 currentValue
이다.
MissionUtils.Console.readLine
그리고 이 친구가 initialValue
이다.
함수의 동작을 순서로 나타내면 다음과 같다.
MissionUtils.Console.readLine
이callback
의 첫번째 인수로 전달된다.mockImplementationOnce
을 호출한 후 반환된 값을 acc에 누적한다.answer
의 길이만큼 2번을 반복한다.
이제 거의 다 왔다. mockImplementationOnce
을 찾아보자.
jest 공식 문서를 찾아본 결과 mockImplementationOnce
는 mock function을 한번 실행 시켜주는 역할을 수행함을 알 수 있었다.
const mockQuestions = (answers) => {
MissionUtils.Console.readLine = jest.fn();
answers.reduce((acc, input) => {
return acc.mockImplementationOnce((question, callback) => {
callback(input);
});
}, MissionUtils.Console.readLine);
};
이제 각각 분석한 코드를 하나로 합쳐서 보자.
위 함수의 실행 순서는 다음과 같다.
- answers를 인자로 입력 받는다.
MissionUtils.Console.readLine
을 mock function으로 만든다.- answers의 요소들 각각을 mock function에 들어가는 콜백 함수의 인자로 전달한다.
종합해보자면 위 함수에 answers 배열을 인자로 넘겨 호출하면 MissionUtils.Console.readLine
의 콜백함수의 인자에 answers 배열의 각 요소를 넘겨주는 역할을 한다. 이때, 콜백함수의 인자는 사용자의 입력값을 의미하므로 이 함수의 실행은 answers 배열을 통해 임의대로 사용자 입력을 조작할 수 있게 해준다.
다음 포스팅에는 테스트 코드로 주어진
const mockRandoms = (numbers) => {
MissionUtils.Random.pickUniqueNumbersInRange = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, MissionUtils.Random.pickUniqueNumbersInRange);
};
이 mockRandoms
를 분석해보겠다.