Javascript 使用Jest在vanilla js中测试AJAX

Javascript 使用Jest在vanilla js中测试AJAX,javascript,unit-testing,xmlhttprequest,jestjs,Javascript,Unit Testing,Xmlhttprequest,Jestjs,我正试图用JAST在香草JavaScript中测试XMLHttpRequesting函数。这是对一个模型函数的单元测试。该函数正在对mashape.com和著名的quote API进行XMLHttpRequest 这是我的模型: const QuoteModel = function(quote) { this.quote = quote; this.savedQuotes = []; this.quoteChanged = new Observer(); thi

我正试图用JAST在香草JavaScript中测试XMLHttpRequesting函数。这是对一个模型函数的单元测试。该函数正在对mashape.com和著名的quote API进行XMLHttpRequest

这是我的模型:

const QuoteModel = function(quote) {
    this.quote = quote;
    this.savedQuotes = [];
    this.quoteChanged = new Observer();
    this.quoteSaved = new Observer();
    this.quoteDeleted = new Observer();
};

QuoteModel.prototype.changeQuote = function(quote) {
    this.quote = quote;
    this.quoteChanged.notify(this.quote);
};

QuoteModel.prototype.fetchQuote = function(url, apiKey = null) {
    const xhr = new XMLHttpRequest();
    let data;

    // QuoteModel
    const self = this;

    xhr.onload = function() {

        if (xhr.status >= 200 && xhr.status < 300) {
            data = JSON.parse(this.response)[0];
            self.changeQuote(data);

        } else {
            data = 'Bad response';
        }

    };

    xhr.onerror = function() {
        data = 'Error fetching quote';
    };

    xhr.open('GET', url, true);

    if (apiKey != null) xhr.setRequestHeader('X-Mashape-Key', apiKey);

    xhr.send();

};

QuoteModel.prototype.getQuote = function() {
    return this.quote;
};

QuoteModel.prototype.tweet = function() {
    // Opens a tweet window..
};

QuoteModel.prototype.loadSavedQuotes = function() {
    // Load quotes from localStorage..
};

QuoteModel.prototype.saveQuote = function(quote) {
    // Saves quotes to localStorage..
};
当我运行测试时,我得到以下信息:

 FAIL  test/QuoteModel.test.js
  ✕ should make XMLHttpRequest to get new quote (16ms)
  ✓ should have quote set (1ms)
  ✓ should change quote on request

  ● should make XMLHttpRequest to get new quote

    expect(received).toEqual(expected)

    Expected value to equal:
      {"author": "Pelle the Cat", "quote": "A fetched quote is as good as any quote."}
    Received:
      {"author": "Michael Krøyserth-Simsø", "quote": "I will never be quoted!"}

    Difference:

    - Expected
    + Received

      Object {
    -   "author": "Pelle the Cat",
    -   "quote": "A fetched quote is as good as any quote.",
    +   "author": "Michael Krøyserth-Simsø",
    +   "quote": "I will never be quoted!",
      }

      23 | test('should make XMLHttpRequest to get new quote', () => {
      24 |     model.fetchQuote('https://andruxnet-random-famous-quotes.p.mashape.com/?cat=famous&count=10', config.API_KEY);
    > 25 |     expect(model.quote).toEqual({
         |                         ^
      26 |         quote: 'A fetched quote is as good as any quote.',
      27 |         author: 'Pelle the Cat'
      28 |     });

      at Object.<anonymous> (test/QuoteModel.test.js:25:25)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total
Snapshots:   0 total
Time:        1.985s
Ran all test suites matching /test\/QuoteModel.test.js/i.
npm ERR! Test failed.  See above for more details.
失败测试/QuoteModel.test.js
✕ 应进行XMLHttpRequest以获取新报价(16ms)
✓ 应设置报价单(1ms)
✓ 应根据要求更改报价
● 应进行XMLHttpRequest以获取新报价
预期(已收到)。toEqual(预期)
期望值等于:
{“author”:“Pelle the Cat”,“quote”:“获取的引用与任何引用一样好。”
收到:
{“作者”:“迈克尔·克雷瑟斯·西姆斯”,“引用”:“我永远不会被引用!”
区别:
-期望
+收到
反对{
-“作者”:“佩勒猫”,
-“报价”:“获取的报价与任何报价一样好。”,
+“作者”:“迈克尔·克雷瑟斯·西姆斯”,
+“引用”:“我永远不会被引用!”,
}
23 | test('应进行XMLHttpRequest以获取新报价',()=>{
24 |型号.FETCQUOTE('https://andruxnet-random-famous-quotes.p.mashape.com/?cat=famous&count=10,config.API_KEY);
>25 | expect(model.quote)。toEqual({
|                         ^
26 | quote:“获取的报价与任何报价一样好。”,
27 |作者:《佩尔猫》
28 |     });
反对

  • 我错过了什么
  • 我至少在正确的轨道上吗
  • 这是测试AJAX的正确方法吗
(这是中的“随机报价机”项目。我知道这有点过分,但我真的很想用MVC制作一个前端应用。) 我自己解决了

答案在中。只有答案不被接受为解决方案

let open, send, status, onload, setRequestHeader, response;
function createXHRmock() {
    open = jest.fn();
    status = 200;
    setRequestHeader = jest.fn();
    response = JSON.stringify([{
        quote: 'A fetched quote is as good as any quote.',
        author: 'Pelle the Cat'
    }]);
    // be aware we use *function* because we need to get *this* 
    // from *new XmlHttpRequest()* call
    send = jest.fn().mockImplementation(function(){   
        onload = this.onload.bind(this);
        onerror = this.onerror.bind(this);
        setRequestHeader = this.setRequestHeader.bind(this);
    });

    const xhrMockClass = function () {
        return {
            open,
            send,
            status,
            setRequestHeader,
            response
        };
    };

    window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
}
必须将其更改为
jest.fn().mockImplementation
,并添加
状态、setRequestHeader、response
,以使其按我想要的方式工作。
现在我可以测试是否调用了
model.changeQuote
,并更改了报价。希望有一天这对任何人都有用。

有些人可能会发现这对react有用


    import { act, renderHook } from "@testing-library/react-hooks"
    import { xxx } from "../xxx"
    
    
    describe("xxxx", () => {
      it("should xxx", async () => {
    
        let onload
        const xhrMock = {
          open: jest.fn(),
          setRequestHeader: jest.fn(),
          onreadystatechange: jest.fn(),
          // dont change to arrow function ... does not work
          send: jest.fn(function() {
            onload = this.onload
          }),
          readyState: 4,
          responseText: "text",
          status: 200
        }
    
        window.XMLHttpRequest = jest.fn(() => {
          return xhrMock
        })
    
        const { result } = renderHook(() => xxx())
        act(() => {
          onload()
        })


    import { act, renderHook } from "@testing-library/react-hooks"
    import { xxx } from "../xxx"
    
    
    describe("xxxx", () => {
      it("should xxx", async () => {
    
        let onload
        const xhrMock = {
          open: jest.fn(),
          setRequestHeader: jest.fn(),
          onreadystatechange: jest.fn(),
          // dont change to arrow function ... does not work
          send: jest.fn(function() {
            onload = this.onload
          }),
          readyState: 4,
          responseText: "text",
          status: 200
        }
    
        window.XMLHttpRequest = jest.fn(() => {
          return xhrMock
        })
    
        const { result } = renderHook(() => xxx())
        act(() => {
          onload()
        })