使用单例在无尽的 setInterval(,0) 中对 process.exit 的开玩笑测试调用
Posted
技术标签:
【中文标题】使用单例在无尽的 setInterval(,0) 中对 process.exit 的开玩笑测试调用【英文标题】:Jest test call to process.exit in endless setInterval(,0) using Singletons 【发布时间】:2022-01-06 22:58:36 【问题描述】:原来的项目有一个类有一些长期运行的工作。这是在服务器上的进程中完成的,偶尔会让SIGINT
s 停止。一旦发生这种情况,我想保持状态。这就是为什么我将工作放入setInterval(,0)
包装器中,以便事件循环获取中断。这在while(true)
-loop 中不起作用。
现在我想测试一下主题是否正常工作 - 在这种情况下:验证输入。 可以在here 找到 MWE。
工作在subject.class.ts
:
import writeFileSync from "fs";
import tmpdir from "os";
import join, basename from "path";
export default class Subject
private counter = 0;
public doWork(inputdata: string): void
if (!inputdata)
process.exit(100); // exit if no input was given
setInterval(() =>
this.counter++;
if (this.counter > 10e2)
process.exit(0);
if (this.counter % 10e1 == 0)
console.log(this.counter);
, 0);
public persist(): void
const data = JSON.stringify(this.counter);
const path = join(tmpdir(), basename(__filename));
writeFileSync(path, data);
console.log(`Persisted to $path`);
我创建一个new Subject
,设置中断处理程序并调用subj.doWork("peanut")
方法在main.ts
开始工作:
import Subject from "./subject.class";
const subj = new Subject();
process.on("exit", (exitcode: number) =>
if (exitcode == 0)
process.stdout.write(`\nDone Success. :)\n`);
else
process.stderr.write(`\nDone with code: $exitcode\n`);
subj.persist();
process.stdout.write("exiting.");
process.exit(exitcode);
);
process.on("SIGINT", (signal: "SIGINT") =>
process.stdout.write(`\ncaught $signal`);
process.exit(13);
);
subj.doWork("peanut");
所有工作文件。为了测试对process.exit
的调用,我在其上创建了一个jest.spyOn
,它从it
中的tests/subject.test.ts
中调用done
函数
import Subject from "../src/subject.class";
describe("subject test suite", () =>
jest.spyOn(console, "log").mockImplementation();
it("should exit on invalid input", (done) =>
const spyExit = jest.spyOn(process, "exit").mockImplementation(((
nu: number
) =>
expect(nu).toEqual(100);
spyExit.mockRestore();
done();
) as any);
expect(() => new Subject().doWork("")).not.toThrow();
);
it.skip("should work on valid input", (done) =>
const spyExit = jest.spyOn(process, "exit").mockImplementation(((
nu: number
) =>
expect(nu).toEqual(0); // does not matter because it is not checked anyway
spyExit.mockRestore();
done();
) as any);
expect(() => new Subject().doWork("valid")).not.toThrow();
);
);
问题是,Jest
在done
被第一个it
调用之前退出; Jest
然后在命令行上抱怨已尝试写入日志。当取消skip
第二种情况时,第一种情况有效,因为Jest
还活着。但是,expect.toEqual(0);
也永远不会被调用。这个0
可以是任何数字,但测试仍然没有失败(它只是打印到控制台,process.exit
以“0”调用,但行号错误)。
如何测试这个Subject的工作方法?
【问题讨论】:
【参考方案1】:所以有几件事要说:
-
因为
process.exit()
不再退出(因为它被模拟了),所以必须清除间隔并且我们需要在调用process.exit()
时返回。
在 Jest 测试中,计时器可以/应该/需要被伪造。 (见Jest Documentation on Timer-Mocks)
根据测试设置(我在实际项目中使用singleton
模式),每个test
/it
有一个测试文件有助于确保它们不会与单例实例冲突。似乎每个文件有一个线程(然后它们并行运行)。
试试jest --runInBand
。
subject.class.ts
:
if (!inputdata)
return process.exit(100); // note 'return'
const id = setInterval(()=> // save interval-id
this.counter++;
if (this.counter > 10e2)
clearInterval(id); // clear before exit'ing
return process.exit(0); // note 'return'
if (this.counter % 10e1 == 0)
console.log(this.counter);
, 0);
tests/subject.test.ts
:
beforeAll(() =>
jest.useFakeTimers();
);
afterAll(() =>
jest.useRealTimers();
);
// and in the test then run the Timers
expect(() => new Subject().doWork("")).not.toThrow();
jest.runAllTimers();
【讨论】:
以上是关于使用单例在无尽的 setInterval(,0) 中对 process.exit 的开玩笑测试调用的主要内容,如果未能解决你的问题,请参考以下文章