基本上,我目前正在为一个函数编写单元测试,该函数使用 AJV 模式检查 json
文件是否有效。问题是,针对架构的检查在浏览器中有效,但在测试中无效。
无效文件错误
export class InvalidFileError extends Error {
constructor(message) {
super(message)
this.name = "InvalidFileError"
}
}
我要测试的功能
export function importFile(e, importScenarios, importDevices) {
const file = e.target.files[0]
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.onload = () => { // File loaded
const fileContent = JSON.parse(fileReader.result)
const ajv = new Ajv({allErrors: true})
const validate = ajv.compile(schema)
const contentIsValid = validate(fileContent)
console.log("Content is valid: ", contentIsValid)
if (contentIsValid) {
importScenarios(fileContent.scenarios)
importDevices(fileContent.devices)
} else {
throw new InvalidFileError("This file doesn't match the schema")
}
}
}
我目前写的测试
describe("Does Importing a file work properly?", () => {
let file
let e = {
target: {
files: []
}
}
let importScenarios = () => {}
let importDevices = () => {}
test("Does it work with a file matching the schema?", () => {
file = new Blob(correctTestContent, { type: "application/json" })
e.target.files.push(file)
expect(() => {
FileManager.importFile(e, importScenarios, importDevices)
}).not.toThrow(InvalidFileError)
})
test("Does it work with a file not matching the schema??", () => {
file = new Blob(incorrectTestContent, { type: "application/json" })
e.target.files.push(file)
expect(() => {
FileManager.importFile(e, importScenarios, importDevices)
}).toThrow(InvalidFileError)
})
afterEach(() => {
e.target.files = []
})
})
当我在浏览器中使用此功能时,通过上传无效文件,它会抛出错误,而如果我上传有效文件,则不会。 这在测试中应该是完全一样的,但不幸的是它不是。
最佳答案
问题是您尝试测试的代码是异步的,而您编写的测试不是。
当您运行测试时,FileReader
的onload
回调不会在相应测试执行期间执行。相反,它在测试执行后被调用。事实上,因为你有声明:
console.log("Content is valid: ", contentIsValid)
在 importFile
方法中,您应该会在控制台中看到如下消息:
Cannot log after tests are done. Did you forget to wait for something async in your test?
您需要进行测试 asynchronous , 以便他们等待 onload
回调执行。不幸的是,您的代码很难按原样进行测试,因为您无法知道 onload
回调何时执行,因此也很难在测试中等到那一刻。
解决此问题的一种方法是将异步代码包装在 Promise 中并返回它,以便我们可以等到 promise 完成。使用这种方法,您的 importFile
将类似于:
export function importFile(e, importScenarios, importDevices) {
const file = e.target.files[0]
const fileReader = new FileReader()
fileReader.readAsText(file)
return new Promise((resolve, reject) => {
fileReader.onload = () => { // File loaded
const fileContent = JSON.parse(fileReader.result)
const ajv = new Ajv({allErrors: true})
const validate = ajv.compile(schema)
const contentIsValid = validate(fileContent)
if (contentIsValid) {
importScenarios(fileContent.scenarios)
importDevices(fileContent.devices)
resolve()
} else {
reject(new InvalidFileError("This file doesn't match the schema"))
}
}
});
}
然后,您可以通过在测试中返回 Promise 来测试此方法(这样 jest 就知道它必须等到 promise 被解析或拒绝):
let importScenarios = jest.fn()
let importDevices = jest.fn()
test("Does it work with a file matching the schema?", () => {
expect.assertions(2);
file = new Blob(correctTestContent, { type: "application/json" })
e.target.files.push(file)
return FileManager.importFile(e, importScenarios, importDevices).then(() => {
expect(importScenarios).toHaveBeenCalledTimes(1);
expect(importDevices).toHaveBeenCalledTimes(1);
});
});
test('Does it work with a file not matching the schema??', () => {
expect.assertions(1);
file = new Blob(incorrectTestContent, { type: "application/json" })
e.target.files.push(file)
return FileManager.importFile(e, importScenarios, importDevices).catch((e) => {
expect(e).toBeInstanceOf(InvalidFileError);
});
});
请注意,我已经重新定义了变量 importScenarios
和 importDevices
,因此它们是 mock functions我们可以检查他们是否被调用。另外,请注意使用 expect.assertions
来验证是否调用了一定数量的断言。
最后,请注意,如果您重新定义 importFile
以便它返回 promise ,您可能必须更改调用它的地方以处理拒绝情况。你在哪里:
try {
FileManager.importFile(e, importScenarios, importDevices)
} catch(e) {
// Some treatment of your exception
}
你需要:
FileManager.importFile(e, importScenarios, importDevices).catch(e => {
// Some treatment of your exception
})
关于javascript - 如何修复在使用 Jest 进行测试时未正确检查 ajv 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56075156/