【问题标题】:XMLHttpRequest testing in JestJest 中的 XMLHttpRequest 测试
【发布时间】:2015-04-19 12:32:30
【问题描述】:

我想测试 AJAX 方法(vanilla XHR),但我找不到使用 Jest 框架的方法。我为 Jasmine 找到了 mock-ajax.js。问题是我找不到安装它的方法。

有没有更好的方法在 Jest 中对 Ajax 函数进行单元测试?

【问题讨论】:

    标签: javascript ajax jestjs xmlhttprequest


    【解决方案1】:

    这是一个使用 Jest 26 的 TypeScript 示例:

    function performRequest(callback: any) {
      const xhr = new XMLHttpRequest();
      xhr.open('GET', 'https://example.com/');
      xhr.onreadystatechange = () => {
        if (xhr.readyState !== 4 || xhr.status !== 200) return;
        callback(xhr.response);
      };
      xhr.responseType = 'json';
      xhr.setRequestHeader('Accept', 'application/json');
      xhr.send(null);
    }
    
    describe('request', () => {
      const xhrMock: Partial<XMLHttpRequest> = {
        open: jest.fn(),
        send: jest.fn(),
        setRequestHeader: jest.fn(),
        readyState: 4,
        status: 200,
        response: 'Hello World!'
      };
    
      jest.spyOn(window, 'XMLHttpRequest').mockImplementation(() => xhrMock as XMLHttpRequest);
      const callback = jest.fn();
      performRequest(callback);
      expect(xhrMock.open).toBeCalledWith('GET', 'https://example.com/');
      expect(xhrMock.setRequestHeader).toBeCalledWith('Accept', 'application/json');
      (xhrMock.onreadystatechange as EventListener)(new Event(''));
      expect(callback.mock.calls).toEqual([['Hello World!']]);
    });
    

    【讨论】:

    • 与大多数其他解决方案相反,这个解决方案可以正确使用 TypeScript,尤其要感谢 Partial 类型。
    【解决方案2】:

    我就是这样做的。

    test.js

    const open = jest.fn();
    const onload = jest.fn((x) => {/* <your response data> */});
    const onerror = jest.fn();
    const send = jest.fn(function(){
        this.onload()
    })
    
    const xhrMockClass = function () {
        return {
            open,
            send,
            onerror,
            onload
        };
    };
    
    global.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
    
    // ...
    
    test('Should make a request', () => {
     // do stuff to make request
    
     expect(send).toHaveBeenCalled()
     expect(onload).toHaveBeenCalledWith(/* <your response data> */)
     expect(open).toHaveBeenCalledWith('GET', 'some/url', true)
    })
    

    【讨论】:

      【解决方案3】:

      这是一个函数,它将创建一个 XMLHttpRequest 模拟并将其安装到 window 中。

      const mockXMLHttpRequest = () => {
        const mock = {
          open: jest.fn(),
          addEventListener: jest.fn(),
          setRequestHeader: jest.fn(),
          send: jest.fn(),
          getResponseHeader: jest.fn(),
      
          upload: {
            addEventListener: jest.fn(),
          },
        };
      
        window.XMLHttpRequest = jest.fn(() => mock);
      
        return mock;
      };
      

      然后您可以在这样的测试中使用它:

      const mock = mockXMLHttpRequest();
      
      // Get the upload progress callback
      // This assumes you attach your listeners in a stable order
      expect(mock.upload.addEventListener).toHaveBeenCalledTimes(1);
      const [[, progress]] = mock.upload.addEventListener.mock.calls;
      
      // Get the load callback
      expect(mock.addEventListener).toHaveBeenCalledTimes(1);
      const [[, load]] = mock.addEventListener.mock.calls;
      
      expect(mock.open).toHaveBeenCalled();
      expect(mock.send).toHaveBeenCalled();
      
      // Fire a progress event
      progress({ loaded: 12, total: 100 });
      
      // ...
      
      mock.status = 201;
      mock.getResponseHeader.mockReturnValue('application/json');
      mock.response = JSON.stringify({ id: 111 });
      
      // Fire a load event
      load();
      
      // ...
      

      【讨论】:

        【解决方案4】:

        如上所述,您不需要额外的库:

         // mocks.js is where you could put your mocks (example below)
         const mocks = require('./mocks.js')
        
         test('XHR test', () => {
           let xhrMock = {
              open: jest.fn(),
              setRequestHeader: jest.fn(),
              onreadystatechange: jest.fn(),
              send: jest.fn(),
              readyState: 4,
              responseText: JSON.stringify(mocks),
              status: 200
            }
        
           window.XMLHttpRequest = jest.fn(() => xhrMock)
        
           sendData().then((response) => {
              // You could do you checks here. Some examples:
              expect(xhrMock.setRequestHeader).toBeCalledWith('Cache-Control', 'no-cache')
              expect(xhrMock.open).toBeCalledWith('POST', 'you_api_url.com/end_point/')
              expect(xhrMock.withCredentials).toBe(false)
        
              let formData = new FormData()
              formData.append('firstParam', 'firstParamValue')  
        
              expect(yoloRequestMock.send).toBeCalledWith(formData)
              expect(JSON.stringify(response)).toBe(JSON.stringify(mocks))
           })
           // when onload is called, resolve and reject has been initialed.
           xhrMock.onreadystatechange()
        
            // the following function is the one which sends the request (to be tested)
            function sendData() {
               return new Promise(function(resolve, reject) {
                 let formData = new FormData()
                 formData.append('firstParam', 'firstParamValue')
                 let xhr = new XMLHttpRequest()
                 xhr.withCredentials = false
                 xhr.onreadystatechange = function () {
                   if (this.readyState === 4) {
                     if(xhr.status === 200) {
                       resolve(JSON.parse(xhr.responseText))
                     } else {
                       reject(xhr.responseText)
                     }
                   }
                 }
                 xhr.open('POST', T2S_VISUAL_SEARCH_PARAMS.t2sVisualSeacchApiUrl)
                 xhr.setRequestHeader("Cache-Control", "no-cache");
                 xhr.send(formData)
               })
            }
          }
        

        文件mocks.js 包含模拟:

        module.exports =
          {
            response: {
              body: { ... }
            }
          }
        

        【讨论】:

          【解决方案5】:

          jest api 发生了一些变化。这就是我使用的。它什么也没做,但足以渲染我的组件。

          const xhrMockClass = () => ({
            open            : jest.fn(),
            send            : jest.fn(),
            setRequestHeader: jest.fn()
          })
          
          window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
          

          在测试文件中:

          import '../../__mock__/xhr-mock.js'

          【讨论】:

            【解决方案6】:

            您可以在 Jest 中测试 XHR,而无需额外的软件包。这是为 XMLHttpRequest 创建模拟对象的辅助函数:

            let open, send, onload, onerror;
            
            function createXHRmock() {
                open = jest.genMockFn();
            
                // be aware we use *function* because we need to get *this* 
                // from *new XmlHttpRequest()* call
                send = jest.genMockFn().mockImpl(function(){   
                    onload = this.onload.bind(this);
                    onerror = this.onerror.bind(this);
                });
            
                const xhrMockClass = function () {
                    return {
                        open,
                        send
                    };
                };
            
                window.XMLHttpRequest = jest.genMockFn().mockImpl(xhrMockClass);
            }
            

            您可以在测试中使用它,如下所示:

            it('XHR success', () => {
                createXHRmock();
            
                // here you should call GET request
            
                expect(open).toBeCalledWith('GET', 'http://example.com', true);
                expect(send).toBeCalled();
            
                // call onload or onerror
                onload();
            
                // here you can make your assertions after onload
            });
            

            【讨论】:

            • 我认为这是来自docsjest.fn().mockImplementation
            猜你喜欢
            • 2022-01-20
            • 1970-01-01
            • 2016-12-14
            • 2015-08-05
            • 2023-03-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-09
            相关资源
            最近更新 更多