有時候我們需要不傳送請求就能完成前端的業務邏輯測試,而許多的業務邏輯都會需要呼叫到後端的API介面。那如何能mock我們所需要的data就是一個問題。當我們能有一個良好的測試環境之後,只要保證後端的介面沒有問題,那我們就可以保證業務邏輯也沒有問題。
所以我們對API的整合測試有以下幾個要求
1.不傳送請求,返回本地假資料
2.釋出前通過CI跑unit test,通過則釋出上線
如何實現?
首先一般我們在network部分都會進行封裝,假設在project中封裝瞭如下的請求工具
// http tool
export default function http() {
// some implement
}
複製程式碼
既然我們不能傳送真實請求,那我們就需要類似能攔截的東西,攔截也可以通過mock代替。於是我們可以通過jest.mock
方法來做。
jest.mock
// api/http.js
// real fn
export default function http() {
console.log('real');
//...
}
複製程式碼
// api/__mocks__/http.js
// fake fn
export default function http() {
console.log('fake');
}
複製程式碼
// some.test.js
jest.mock('../http')
import http from '../http'
http() // 這裡log的是fake,而不是real
複製程式碼
這個就是jest.mock
的作用。
正事
明白了這個後就好辦了。專案目錄如下:
-- api
|-- __mockData__
|-- user.data.js
|-- __mocks__
|-- http.js
|-- __tests__
|-- user.test.js
|-- http.js
|-- profile // profile 業務模組
|-- user.js // 獲取使用者資訊
複製程式碼
而我們的fake檔案其實主要做的事情就是根據請求url,method,status等,去讀取對應的本地假資料。大致如下。
// ./api/__mocks__/http.js
// 直接讀取本地假資料
let statusCode;
export function setStatus(code) {
statusCode = code;
}
export default function http({ url = "", data = {}, method = "get" }) {
return new Promise((resolve, reject) => {
const lastSlash = url.lastIndexOf("/");
const module = url.substring(lastSlash + 1);
const mockData = require(`../__mockData__/${module}.data`).default;
const result = mockData[`${method.toUpperCase()} ${statusCode}`];
process.nextTick(
() => (statusCode === 200 ? resolve(result) : reject(result))
);
});
}
複製程式碼
mockData資料夾則就是放我們的假資料,在這我們可以假設定義如下資料結構,來模擬我們的response
// ./api/__mockData__/user.js
export default {
'GET 200': {
code: 0,
msg: 'ok',
data: {
username: '二哲',
age: 18,
},
},
'POST 200': {
code: 0,
msg: 'xxx',
},
'GET 400': {
msg: 'invald params',
code: -1,
},
'GET 401': {},
};
複製程式碼
最後看下我們的 unit test 如何寫
// ./api/__test__/user.test.js
jest.mock('../http') // jest 會自動搜尋目錄下的 __mocks__裡的檔案
import http from '../http';
describe('user api test', () => {
it("user GET should be 200", async () => {
setStatus(200);
const result = await http({
url,
method: "get"
});
expect(result.data.username).toBe("Kodo");
});
})
複製程式碼
實現了這個有什麼用?
假設/user
介面返回得資料可能是這樣
{
"username": "二哲",
"age": 18,
}
複製程式碼
而我們前端service層為UI層提供了一個initUserData的方法,initUserData方法裡的操作是當age
為18,那就要返回19。
所以我們在Jest則可以直接這樣測試
// ./api/__test__/user.test.js
jest.mock('../http') // jest 會自動搜尋目錄下的 __mocks__裡的檔案
import { setStatus } from './http';
import { initUserData } from '../user'
describe('user api test', () => {
it("if user age is 18, age should be 19", async () => {
expect.assertions(1);
setStatus(200);
const result = await initUserData();
// console.log(result);
expect(result.data.age).toBe(19);
});
// test catch
it("initUserData 400", async () => {
expect.assertions(1);
setStatus(400);
const result = await initUserData();
expect(result.msg).toBe("invald params");
});
})
複製程式碼
這樣我們使用Jest就可以完成對業務邏輯的測試,Unit test在大型專案中非常需要,每當提交一個feature時,可以跑完所有測試,會讓你非常有安全感,極大提升了專案的穩定性。
TIP
真正的方法(http),與mock的方法http,檔案必須同名,然後放在mocks資料夾下即可。如果不同名使用jest.mock()則會失敗。
以上例子都在這 jest-api-test