用Jest在vanilla js中测试AJAX



我正在尝试用Jest在vanilla JavaScript中测试XMLHttpRequest函数。这是对其中一个模型函数的单元测试。该函数正在向 mashape.com randsom-fames-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..
};

因此,fetchQuote 函数正在发出 AJAX 请求,并使用收到的报价调用 changQuote。

在模型的单元测试中,我得到了这个:

import QuoteModel from '../js/QuoteModel';
import config from '../config.js';
const model = new QuoteModel({
quote: 'I will never be quoted!',
author: 'Michael Krøyserth-Simsø'
});
// https://stackoverflow.com/questions/28584773/xhr-testing-in-jest
const xhrMockClass = () => ({
open: jest.fn(),
send: jest.fn(),
setRequestHeader: jest.fn(),
status: 200,
response: JSON.stringify([{
quote: 'A fetched quote is as good as any quote.',
author: 'Pelle the Cat'
}])
});
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
// fetchQuote - ajax call to get quote is successfull
test('should make XMLHttpRequest to get new quote', () => {
model.fetchQuote('https://andruxnet-random-famous-quotes.p.mashape.com/?cat=famous&count=10', config.API_KEY);
expect(model.quote).toEqual({
quote: 'A fetched quote is as good as any quote.',
author: 'Pelle the Cat'
});
});

当我运行测试时,我得到这个:

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.

在我看来,对 model.fetchQuote 的调用应该用模拟函数中的新报价来更改 this.quote。 我从这个静止中得到了这个想法 - 开玩笑中的 XHR 测试 .

  • 我在这里错过了什么?
  • 我至少走在正确的轨道上吗?
  • 这是测试 AJAX 的正确方法吗?

(这是FreeCodeCamp中的"随机报价机"项目。我知道这有点矫枉过正,但我真的很想用 MVC 制作一个前端应用程序。 存储库

我自己解决了。

答案是在 Jest 的 XHR 测试中。只有答案不被接受为解决方案。

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并添加status, 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()
})

最新更新