Как исправить некорректную проверку схемы ajv при тестировании с Jest - PullRequest
2 голосов
/ 10 мая 2019

По сути, я сейчас пишу модульный тест для функции, которая проверяет, является ли json -файл действительным, используя схему AJV.Проблема в том, что проверка по схеме работает в браузере, но не в тесте.

InvalidFileError

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 = []
    })
})

Когда я использую эту функцию в браузере, загружая неверный файл, он выдает ошибку, и если я загружаю действительный файл, он делаетне.Это должно быть точно так же в тесте, но, к сожалению, это не так.

1 Ответ

0 голосов
/ 10 мая 2019

Проблема в том, что код, который вы пытаетесь протестировать, является асинхронным, а написанные вами тесты - нет.

Когда вы запускаете тесты, обратный вызов onload для FileReader невыполняется во время выполнения соответствующего теста.Вместо этого он вызывается после выполнения теста.Фактически, поскольку у вас есть утверждение:

console.log("Content is valid: ", contentIsValid)

внутри метода importFile, вы должны увидеть в консоли сообщение, подобное этому:

Невозможно войти в систему после тестовсделано.Вы забыли дождаться чего-то асинхронного в своем тесте?

Вам нужно сделать свои тесты асинхронными , чтобы они ожидали выполнения обратного вызова onload.К сожалению, ваш код сложно протестировать как есть, потому что у вас нет возможности узнать, когда был выполнен обратный вызов onload, поэтому в этом тесте также сложно ждать до этого момента.

В одну сторонуЧтобы решить эту проблему, нужно заключить ваш асинхронный код в 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"))
            }
        }
    });
}

Затем вы можете протестировать этот метод, вернув Обещание в тесте (так что Jest знает, что он должен ждать, пока обещание не будетразрешено или отклонено):

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, чтобы они были фиктивными функциями , и мы можем проверить, вызваны ли они.Также обратите внимание на использование expect.assertions для проверки того, что вызывается определенное количество утверждений.

Наконец, учтите, что если вы переопределите свой importFile, чтобы он возвращал обещание, вы, скорее всего,должны изменить места, где вы называете это, чтобы рассматривать случай отказа.Где у вас есть:

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