如何测试一个 Node.JS 脚本,它不是一个模块并且在调用时立即运行?
How do I test a Node.JS Script that is not a module and runs as soon as it's called?
我喜欢用 Javascript 代替 bash 脚本。
假设一个名为 start.js 的人为脚本是 运行 使用 node start.js
:
const shelljs = require("shelljs")
if (!shelljs.which("serve")) {
shelljs.echo("'serve' is missing, please run 'npm ci'")
process.exit(1)
}
shelljs.exec("serve -s build -l 3000")
我该如何测试:
- 如果
serve
不可用,则永远不会调用 serve -s build -l 3000
并且程序以代码 1 退出。
- 如果
serve
可用,则调用 serve -s build -l 3000
。
我不介意嘲笑“shelljs”,process.exit
或其他任何东西。
我的主要问题是弄清楚如何在测试套件中 require
或 import
这样一个无功能和无模块的文件,并在每个单独的测试中将其 运行 一次mocks present without 实际上把它变成了一个 CommonJS/ES6 模块。
只是模拟 shelljs
模块,并监视 process.exit
函数。
describe("start.js", () => {
let shelljs
let exitSpy
beforeEach(() => {
jest.mock("shelljs", () => {
return {
exec: jest.fn(),
which: jest.fn(),
echo: jest.fn(),
}
})
shelljs = require("shelljs")
exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {})
});
afterEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
it("should execute process.exit with code is 1 when 'serve' is not existed", () => {
shelljs.which.mockReturnValue(false)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).toHaveBeenCalledWith("'serve' is missing, please run 'npm ci'")
expect(exitSpy).toHaveBeenCalledWith(1)
// expect(shelljs.exec).toHaveBeenCalled() // can not check like that, exitSpy will not "break" your code, it will be work well if you use if/else syntax
});
it("should execute serve when 'serve' is existed", () => {
shelljs.which.mockReturnValue(true)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).not.toHaveBeenCalled()
expect(exitSpy).not.toHaveBeenCalled()
expect(shelljs.exec).toHaveBeenCalledWith("serve -s build -l 3000")
});
})
另一种确保生产代码在调用 process.exit
时被中断的方法。 Mock exit
函数抛出一个错误,那么 expect shelljs.exec
将不会被调用
describe("start.js", () => {
let shelljs
let exitSpy
beforeEach(() => {
jest.mock("shelljs", () => {
return {
exec: jest.fn(),
which: jest.fn(),
echo: jest.fn(),
}
})
shelljs = require("shelljs")
exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {
throw new Error("Mock");
})
});
afterEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
it("should execute process.exit with code is 1 when 'serve' is not existed", () => {
shelljs.which.mockReturnValue(false)
expect.assertions(5)
try {
require("./start")
} catch (error) {
expect(error.message).toEqual("Mock")
}
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).toHaveBeenCalledWith("'serve' is missing, please run 'npm ci'")
expect(exitSpy).toHaveBeenCalledWith(1)
expect(shelljs.exec).not.toHaveBeenCalled()
});
it("should execute serve when 'serve' is existed", () => {
shelljs.which.mockReturnValue(true)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).not.toHaveBeenCalled()
expect(exitSpy).not.toHaveBeenCalled()
expect(shelljs.exec).toHaveBeenCalledWith("serve -s build -l 3000")
});
})
我喜欢用 Javascript 代替 bash 脚本。
假设一个名为 start.js 的人为脚本是 运行 使用 node start.js
:
const shelljs = require("shelljs")
if (!shelljs.which("serve")) {
shelljs.echo("'serve' is missing, please run 'npm ci'")
process.exit(1)
}
shelljs.exec("serve -s build -l 3000")
我该如何测试:
- 如果
serve
不可用,则永远不会调用serve -s build -l 3000
并且程序以代码 1 退出。 - 如果
serve
可用,则调用serve -s build -l 3000
。
我不介意嘲笑“shelljs”,process.exit
或其他任何东西。
我的主要问题是弄清楚如何在测试套件中 require
或 import
这样一个无功能和无模块的文件,并在每个单独的测试中将其 运行 一次mocks present without 实际上把它变成了一个 CommonJS/ES6 模块。
只是模拟 shelljs
模块,并监视 process.exit
函数。
describe("start.js", () => {
let shelljs
let exitSpy
beforeEach(() => {
jest.mock("shelljs", () => {
return {
exec: jest.fn(),
which: jest.fn(),
echo: jest.fn(),
}
})
shelljs = require("shelljs")
exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {})
});
afterEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
it("should execute process.exit with code is 1 when 'serve' is not existed", () => {
shelljs.which.mockReturnValue(false)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).toHaveBeenCalledWith("'serve' is missing, please run 'npm ci'")
expect(exitSpy).toHaveBeenCalledWith(1)
// expect(shelljs.exec).toHaveBeenCalled() // can not check like that, exitSpy will not "break" your code, it will be work well if you use if/else syntax
});
it("should execute serve when 'serve' is existed", () => {
shelljs.which.mockReturnValue(true)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).not.toHaveBeenCalled()
expect(exitSpy).not.toHaveBeenCalled()
expect(shelljs.exec).toHaveBeenCalledWith("serve -s build -l 3000")
});
})
另一种确保生产代码在调用 process.exit
时被中断的方法。 Mock exit
函数抛出一个错误,那么 expect shelljs.exec
将不会被调用
describe("start.js", () => {
let shelljs
let exitSpy
beforeEach(() => {
jest.mock("shelljs", () => {
return {
exec: jest.fn(),
which: jest.fn(),
echo: jest.fn(),
}
})
shelljs = require("shelljs")
exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {
throw new Error("Mock");
})
});
afterEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
it("should execute process.exit with code is 1 when 'serve' is not existed", () => {
shelljs.which.mockReturnValue(false)
expect.assertions(5)
try {
require("./start")
} catch (error) {
expect(error.message).toEqual("Mock")
}
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).toHaveBeenCalledWith("'serve' is missing, please run 'npm ci'")
expect(exitSpy).toHaveBeenCalledWith(1)
expect(shelljs.exec).not.toHaveBeenCalled()
});
it("should execute serve when 'serve' is existed", () => {
shelljs.which.mockReturnValue(true)
require("./start")
expect(shelljs.which).toHaveBeenCalledWith("serve");
expect(shelljs.echo).not.toHaveBeenCalled()
expect(exitSpy).not.toHaveBeenCalled()
expect(shelljs.exec).toHaveBeenCalledWith("serve -s build -l 3000")
});
})