XHR testing in Jest

前端 未结 6 2047
暖寄归人
暖寄归人 2020-12-05 18:41

I want to test AJAX methods (vanilla XHR) and I can\'t find the way to do it with Jest framework. I found mock-ajax.js for Jasmine, the problem

相关标签:
6条回答
  • 2020-12-05 18:56

    This is how I did it.

    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)
    })
    
    0 讨论(0)
  • 2020-12-05 19:08

    You can test XHR in Jest without additional packages. This is helper function which creates mock object for 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);
    }
    

    And you can use it in test as follows:

    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
    });
    
    0 讨论(0)
  • 2020-12-05 19:08

    Here's a TypeScript example working with Jest 26:

    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!']]);
    });
    
    0 讨论(0)
  • 2020-12-05 19:08

    Here's a function that will create a XMLHttpRequest mock and install it into window for you.

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

    Which you can then use in a test like so:

    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();
    
    // ...
    
    0 讨论(0)
  • 2020-12-05 19:16

    The jest api has changed a bit. This is what I use. It doesn't do anything but it's enough to render my components.

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

    and in the test file:

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

    0 讨论(0)
  • 2020-12-05 19:20

    As mentioned you don't need additional libraries:

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

    The file mocks.js contains the mocks:

    module.exports =
      {
        response: {
          body: { ... }
        }
      }
    
    0 讨论(0)
提交回复
热议问题