node.jsでstdinを受け取る関数のテストを書きたいときに、ユーザー側の入力を最初から注入しておきたい。一応、stdinのmockをjestとかvitestに入れるツールもあるけれど、そこまではしなくて良い場合。
例として、こんな関数があったとする。
import { stdin, stdout } from "node:process";
import { createInterface } from "node:readline/promises";
const rl = createInterface({
input: stdin,
output: stdout,
});
const main = async (props: {
interfaceFn: { question: (query: string) => Promise<string> };
}) => {
const { interfaceFn } = props;
const name = await interfaceFn.question("");
console.log(`Hello, ${name}!`);
};
main({ interfaceFn: rl }).catch(console.error);
普通にrl.question("")
でユーザーの入力を受ける。そのユーザーの入力を受ける関数を引数にしているだけ。
それではmockの場合。
const main = async (props: {
interfaceFn: { question: (query: string) => Promise<string> };
}) => {
const { interfaceFn } = props;
const name = await interfaceFn.question("");
console.log(`Hello, ${name}!`);
};
const mock1 = (response: string) => ({
question: () => Promise.resolve(response),
});
main({ interfaceFn: mock1("TEST") }).catch(console.error);
こうしてquestion関数を持つオブジェクトを返す関数を作成し、先に返り値を指定できるようにした。これで、テストのときはmock1関数を使って、それ以外のときはrl.questionを使えばだいたい同じような動きになる。このあと、入力された値をバリデーションしたりとか、いろいろするわけだ。
そんな感じですね。