More on Mocking & Diving Deeper
JavaScript Unit Testing - The Practical Guide 課程筆記
Mocking Global Values & Functions
有一個用到 fetch 方法的 async function 如下:
js
export async function sendDataRequest(data) {
const response = await fetch('https://dummy-site.dev/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
const responseData = await response.json();
if (!response.ok) {
throw new HttpError(response.status, 'Sending the request failed.', responseData);
}
return responseData;
}
fetch 是全域的 API,不屬於任何一個模組,所以沒辦法通過 vi.mock
的方式去替代它,這裡可以使用 vi.stubGlobal
去替代:
js
const testResponseData = { testKey: 'testData' };
const testFetch = vi.fn((url, options) => {
return new Promise((resolve, reject) => {
const testResponse = {
ok: true,
json() {
return new Promise((resolve, reject) => {
resolve(testResponseData);
});
},
};
resolve(testResponse);
});
});
vi.stubGlobal('fetch', testFetch);
describe('sendDataRequest()', () => {
it('should return any available response data', () => {
const testData = { key: 'test' };
return expect(sendDataRequest(testData)).resolves.toEqual(testResponseData);
});
});
接著在來完善一下這個測試,在原始的 fetch 中,如果傳入的 data 是沒辦法被 stringify()
的話,整個 promise 是會直接 reject 的,因此這裡在在針對 testFetch
做一點修改:
js
const testFetch = vi.fn((url, options) => {
return new Promise((resolve, reject) => {
if (typeof options.body !== 'string') {
return reject('Not a string.');
}
const testResponse = {
ok: true,
json() {
return new Promise((resolve, reject) => {
resolve(testResponseData);
});
},
};
resolve(testResponse);
});
});
然後新增一個測試案例:
js
it('should convert the provided data to JSON before sending the request', async () => {
const testData = { key: 'test' };
let errorMessage;
// 使用 try catch 取得錯誤訊息再去做斷言
try {
await sendDataRequest(testData);
} catch (error) {
errorMessage = error;
}
expect(errorMessage).not.toBe('Not a string.');
});
如果要測試一個拋出 HttpError
錯誤的案例,可以使用 mockImplementationOnce
去模擬 testFetch
然後將裡面的 ok
設為 false
:
js
import { HttpError } from './errors';
it('should throw an HttpError in case of non-ok responses', () => {
testFetch.mockImplementationOnce((url, options) => {
return new Promise((resolve, reject) => {
if (typeof options.body !== 'string') {
return reject('Not a string.');
}
const testResponse = {
ok: false,
json() {
return new Promise((resolve, reject) => {
resolve(testResponseData);
});
},
};
resolve(testResponse);
});
});
const testData = { key: 'test' };
return expect(sendDataRequest(testData)).rejects.toBeInstanceOf(HttpError);
});