在 Jest 中测试 XMLHttpRequest onload() 回调功能

Posted

技术标签:

【中文标题】在 Jest 中测试 XMLHttpRequest onload() 回调功能【英文标题】:Testing XMLHttpRequest onload() callback functionality in Jest 【发布时间】:2022-01-20 02:30:55 【问题描述】:

我正在使用 Jest 测试一些纯 html 和 vanilla javascript,并且希望能够模拟 XMLHttpRequest,但我定义的 onload() 回调函数除外。

我找到了一些如何模拟整个 XMLHttpRequest 的示例,但我希望能够模拟除我在代码中定义的 onload 函数之外的所有内容(这就是我要测试的逻辑) .

我想测试的 javascript 函数如下所示:

function foo() 
    document.getElementById("loading").style.display = "block";

    var request = new XMLHttpRequest();
    request.open('POST', '/expected/url', true);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.send();
    request.onload = function () 
        if (request.status === 200) 
            var array = JSON.parse(request.responseText);
            array.forEach(function (object) 
                // do some stuff
            );

            document.getElementById("loading").style.display = "none";
        
        else 
            document.getElementById("loading").style.display = "none";
        
    ;
;

module.exports = 
    foo

我的测试文件看起来像:

var script = require('../fs/script.js');
const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../fs/index.html'), 'utf8');

const createMockXHR = responseJSON => 
    const mockXHR = 
        open: jest.fn(),
        send: jest.fn(),
        setRequestHeader: jest.fn(),
        status: 200,
        readyState: 4,
    ;
    return mockXHR;
;

describe('Foo', function () 
    const oldXMLHttpRequest = window.XMLHttpRequest;
    let mockXHR = null;

    beforeEach(() => 
        document.documentElement.innerHTML = html.toString();
        mockXHR = createMockXHR();
        window.XMLHttpRequest = jest.fn(() => mockXHR);
    );

    afterEach(() => 
        window.XMLHttpRequest = oldXMLHttpRequest;
        jest.resetModules();
    );

    it('is set, section is collapsed', function () 
        mockXHR.response = ' "data": "point"';

        var bar = document.getElementById("bar");
        var elementToCollapse = bar.nextElementSibling;

        script.foo();

        expect(mockXHR.send).toBeCalled();
        expect(mockXHR.onload).toBeCalled();
        // expect some more things
    );
);

我知道上面的内容是错误的,因为 onload() 没有在我的模拟中的任何地方定义,但我发现的唯一可能会引发错误的碎屑:

const createMockXHR = responseJSON => 
    const mockXHR = 
        open: jest.fn(),
        send: jest.fn().mockImplementation(function () 
            onload = this.onload.bind(this);
            onerror = this.onerror.bind(this);
        ),
        setRequestHeader: jest.fn(),
        status: 200,
        readyState: 4,
    ;
    return mockXHR;
;

错误:

TypeError: Cannot read properties of undefined (reading 'bind')

      12 |         send: jest.fn().mockImplementation(function () 
    > 13 |             onload = this.onload.bind(this);

【问题讨论】:

【参考方案1】:

虽然没有从技术上回答这个问题,但我已经解决了我无法测试 onload() 内联函数的问题,方法是将逻辑放在单独的函数中并测试:

function foo_logic(request) 
    // the logic


function foo() 
    document.getElementById("loading").style.display = "block";

    var request = new XMLHttpRequest();
    request.open('POST', '/expected/url', true);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.send();
    request.onload = function ()  foo_logic(request) ;
;

module.exports = 
    foo_logic,
    foo

然后测试:

var script = require('../fs/script.js');
const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../fs/index.html'), 'utf8');

const createMockXHR = responseJSON => 
    const mockXHR = 
        open: jest.fn(),
        send: jest.fn(),
        setRequestHeader: jest.fn(),
        status: 200,
        readyState: 4,
    ;
    return mockXHR;
;

describe('Foo', function () 
    const oldXMLHttpRequest = window.XMLHttpRequest;
    let mockXHR = null;

    beforeEach(() => 
        document.documentElement.innerHTML = html.toString();
        mockXHR = createMockXHR();
        window.XMLHttpRequest = jest.fn(() => mockXHR);
    );

    afterEach(() => 
        window.XMLHttpRequest = oldXMLHttpRequest;
        jest.resetModules();
    );

    it('is set, section is collapsed', function () 
         mockXHR.responseText = JSON.stringify(
            data: "point"
        );

        var bar = document.getElementById("bar");
        var elementToCollapse = bar.nextElementSibling;

        script.foo_logic(mockXHR);

        // expect some things about bar and elementToCollapse
    );
);

【讨论】:

以上是关于在 Jest 中测试 XMLHttpRequest onload() 回调功能的主要内容,如果未能解决你的问题,请参考以下文章

在使用 jest 的 vue 测试中找不到模块“@jest/globals”

如何在 jest 测试用例 +VueJS +Jest 中更改 $route

一个文件中的测试是不是在 Jest 中并行运行?

Jest单元测试进阶

在 Jest 中动态生成测试

在异步 Jest 测试中是不是需要完成?