javascript - 如何修复在使用 Jest 进行测试时未正确检查 ajv 模式

标签 javascript jestjs ajv

基本上,我目前正在为一个函数编写单元测试,该函数使用 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 = []
    })
})

当我在浏览器中使用此功能时,通过上传无效文件,它会抛出错误,而如果我上传有效文件,则不会。 这在测试中应该是完全一样的,但不幸的是它不是。

最佳答案

问题是您尝试测试的代码是异步的,而您编写的测试不是。

当您运行测试时,FileReaderonload 回调不会在相应测试执行期间执行。相反,它在测试执行后被调用。事实上,因为你有声明:

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);
    });
});

请注意,我已经重新定义了变量 importScenariosimportDevices,因此它们是 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/

相关文章:

javascript - 我在使用 jquery 将数据从 localStorage 获取回表单以进行更新时遇到问题?

jestjs - 使用 Jest 进行测试时如何忽略 "-!svg-react-loader!./path/to/my.svg"而无需将所有内容与 webpack 捆绑在一起

reactjs - 使用 Jest 和 Enzyme 测试 React 组件 : where to test whether component props were given

javascript - 对象数组的 AJV 模式验证

javascript - 需要更好的 JSON 模式来验证稀疏矩阵数据对象

javascript - 单击切换类

javascript - 同步 2 HTML 输入范围和数量

javascript - 保存后 Mongoose 更新

reactjs - 如何使用 Jest 和 Enzyme 测试 child 文本组件

javascript - 如何使用 AJV 获取当前/编译的模式?