如何重构我的测试以使用 Jest 的手动模拟功能?
How to refactor my test to use Jest's Manual Mock feature?
我能够使用 Jest 进行基本测试,但是当我尝试重构它以使用 Jest 的手动模拟功能时,测试不再有效。
知道我哪里做错了吗?
感谢您的宝贵时间
错误信息:
TypeError: _backendService.default.post is not a function
16 |
17 | return $axios
> 18 | .post(`${RESOURCE_PATH}/batch_upload/`, formData, {
| ^
19 | headers: {
20 | "Content-Type": "multipart/form-data",
21 | },
在测试中/.../actions.spec.js
:
//import $axios from "@/services/backend-service"; // could not get manual mock to work
import actions from "@/store/modules/transactions/actions";
//jest.mock("@/services/backend-service"); // could not get manual mock to work
// this bit of code works
jest.mock("@/services/backend-service", () => {
return {
post: jest.fn().mockResolvedValue(),
};
});
// this bit of code works:end
describe("store/modules/transactions/actions", () => {
it("uploads transactions succeeds", async() => {
const state = {
commit: jest.fn(),
};
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'}
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToSucceeded');
});
});
在 src/.../__mocks__/backend-service.js
:
const mock = jest.fn().mockImplementation(() => {
return {
post: jest.fn().mockResolvedValue(),
};
});
export default mock;
在 src/.../backend-service.js
:
import axios from "axios";
const API_BASE_URL =
`${process.env["VUE_APP_BACKEND_SCHEME"]}` +
`://` +
`${process.env["VUE_APP_BACKEND_HOST"]}` +
`:` +
`${process.env["VUE_APP_BACKEND_PORT"]}` +
`/` +
`${process.env["VUE_APP_BACKEND_PATH_PREFIX"]}`;
const $axios = axios.create({
baseURL: API_BASE_URL,
headers: {
"Content-Type": "application/vnd.api+json",
},
});
export default $axios;
在 src/.../actions.js
:
import $axios from "@/services/backend-service";
const RESOURCE_NAME = "transaction";
const RESOURCE_PATH = `${RESOURCE_NAME}s`;
export const actions = {
uploadTransactions(state, payload) {
let formData = new FormData();
formData.append("file", payload["file"]);
return $axios
.post(`${RESOURCE_PATH}/batch_upload/`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
state.commit("changeUploadStatusToSucceeded");
})
.catch(function (error) {
if (error.response) {
state.commit("changeUploadStatusToFailed");
}
});
},
};
export default actions;
我尝试查看这些资源中的示例,但对我没有任何帮助:
模拟 Axios 拦截器:
覆盖模拟实现:
- Mock.mockImplementation() not working
Jest 模拟文档:https://jestjs.io/docs/mock-function-api#mockfnmockimplementationfn
Jest 手册模拟文档:
使用第 3 方库:https://vhudyma-blog.eu/3-ways-to-mock-axios-in-jest/
简单动作测试示例:https://lmiller1990.github.io/vue-testing-handbook/vuex-actions.html#creating-the-action
过时的动作测试示例:
为了帮助其他人,我最终只使用间谍而不需要使用手动模拟。
这些是帮助我弄明白的参考资料:
https://silvenon.com/blog/mocking-with-jest/functions
https://silvenon.com/blog/mocking-with-jest/modules/
下面是最终为我工作的示例代码:
在测试中/.../actions.spec.js
:
import $axios from "@/services/backend-service";
import actions from "@/store/modules/transactions/actions";
describe("store/modules/transactions/actions", () => {
let state;
let postSpy;
beforeEach(() => {
state = {
commit: jest.fn(),
};
postSpy = jest.spyOn($axios, 'post')
});
it("uploads transactions succeeds", async() => {
postSpy.mockImplementation(() => {
return Promise.resolve();
});
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'},
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToSucceeded');
});
it("uploads transactions fails", async() => {
postSpy.mockImplementation(() => {
return Promise.reject({
response: true,
});
});
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'},
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToFailed');
});
});
在 src/.../actions.js
:
import $axios from "@/services/backend-service";
const RESOURCE_NAME = "transaction";
const RESOURCE_PATH = `${RESOURCE_NAME}s`;
export const actions = {
uploadTransactions(state, payload) {
let formData = new FormData();
formData.append("account_id", 1); // change to get dynamically when ready
formData.append("file", payload["file"]);
//$axios.interceptors.request.use(function (config) {
// state.commit("changeUploadStatusToUploading");
// return config;
//});
return $axios
.post(`${RESOURCE_PATH}/batch_upload/`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response);
state.commit("changeUploadStatusToSucceeded");
})
.catch(function (error) {
if (error.response) {
state.commit("changeUploadStatusToFailed");
}
});
},
};
export default actions;
我能够使用 Jest 进行基本测试,但是当我尝试重构它以使用 Jest 的手动模拟功能时,测试不再有效。
知道我哪里做错了吗?
感谢您的宝贵时间
错误信息:
TypeError: _backendService.default.post is not a function
16 |
17 | return $axios
> 18 | .post(`${RESOURCE_PATH}/batch_upload/`, formData, {
| ^
19 | headers: {
20 | "Content-Type": "multipart/form-data",
21 | },
在测试中/.../actions.spec.js
:
//import $axios from "@/services/backend-service"; // could not get manual mock to work
import actions from "@/store/modules/transactions/actions";
//jest.mock("@/services/backend-service"); // could not get manual mock to work
// this bit of code works
jest.mock("@/services/backend-service", () => {
return {
post: jest.fn().mockResolvedValue(),
};
});
// this bit of code works:end
describe("store/modules/transactions/actions", () => {
it("uploads transactions succeeds", async() => {
const state = {
commit: jest.fn(),
};
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'}
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToSucceeded');
});
});
在 src/.../__mocks__/backend-service.js
:
const mock = jest.fn().mockImplementation(() => {
return {
post: jest.fn().mockResolvedValue(),
};
});
export default mock;
在 src/.../backend-service.js
:
import axios from "axios";
const API_BASE_URL =
`${process.env["VUE_APP_BACKEND_SCHEME"]}` +
`://` +
`${process.env["VUE_APP_BACKEND_HOST"]}` +
`:` +
`${process.env["VUE_APP_BACKEND_PORT"]}` +
`/` +
`${process.env["VUE_APP_BACKEND_PATH_PREFIX"]}`;
const $axios = axios.create({
baseURL: API_BASE_URL,
headers: {
"Content-Type": "application/vnd.api+json",
},
});
export default $axios;
在 src/.../actions.js
:
import $axios from "@/services/backend-service";
const RESOURCE_NAME = "transaction";
const RESOURCE_PATH = `${RESOURCE_NAME}s`;
export const actions = {
uploadTransactions(state, payload) {
let formData = new FormData();
formData.append("file", payload["file"]);
return $axios
.post(`${RESOURCE_PATH}/batch_upload/`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
state.commit("changeUploadStatusToSucceeded");
})
.catch(function (error) {
if (error.response) {
state.commit("changeUploadStatusToFailed");
}
});
},
};
export default actions;
我尝试查看这些资源中的示例,但对我没有任何帮助:
模拟 Axios 拦截器:
覆盖模拟实现:
- Mock.mockImplementation() not working
Jest 模拟文档:https://jestjs.io/docs/mock-function-api#mockfnmockimplementationfn
Jest 手册模拟文档:
使用第 3 方库:https://vhudyma-blog.eu/3-ways-to-mock-axios-in-jest/
简单动作测试示例:https://lmiller1990.github.io/vue-testing-handbook/vuex-actions.html#creating-the-action
过时的动作测试示例:
为了帮助其他人,我最终只使用间谍而不需要使用手动模拟。
这些是帮助我弄明白的参考资料:
https://silvenon.com/blog/mocking-with-jest/functions
https://silvenon.com/blog/mocking-with-jest/modules/
下面是最终为我工作的示例代码:
在测试中/.../actions.spec.js
:
import $axios from "@/services/backend-service";
import actions from "@/store/modules/transactions/actions";
describe("store/modules/transactions/actions", () => {
let state;
let postSpy;
beforeEach(() => {
state = {
commit: jest.fn(),
};
postSpy = jest.spyOn($axios, 'post')
});
it("uploads transactions succeeds", async() => {
postSpy.mockImplementation(() => {
return Promise.resolve();
});
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'},
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToSucceeded');
});
it("uploads transactions fails", async() => {
postSpy.mockImplementation(() => {
return Promise.reject({
response: true,
});
});
await actions.uploadTransactions(
state,
{'file': 'arbitrary filename'},
)
expect(state.commit).toHaveBeenCalledWith('changeUploadStatusToFailed');
});
});
在 src/.../actions.js
:
import $axios from "@/services/backend-service";
const RESOURCE_NAME = "transaction";
const RESOURCE_PATH = `${RESOURCE_NAME}s`;
export const actions = {
uploadTransactions(state, payload) {
let formData = new FormData();
formData.append("account_id", 1); // change to get dynamically when ready
formData.append("file", payload["file"]);
//$axios.interceptors.request.use(function (config) {
// state.commit("changeUploadStatusToUploading");
// return config;
//});
return $axios
.post(`${RESOURCE_PATH}/batch_upload/`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response);
state.commit("changeUploadStatusToSucceeded");
})
.catch(function (error) {
if (error.response) {
state.commit("changeUploadStatusToFailed");
}
});
},
};
export default actions;