Mar*_*ark 16 javascript jestjs
(这是完整的最小代表:https://github.com/magicmark/jest_question)
鉴于以下应用程序:
SRC/food.js
const Food = {
carbs: "rice",
veg: "green beans",
type: "dinner"
};
export default Food;
Run Code Online (Sandbox Code Playgroud)
SRC/food.js
import Food from "./food";
function formatMeal() {
const { carbs, veg, type } = Food;
if (type === "dinner") {
return `Good evening. Dinner is ${veg} and ${carbs}. Yum!`;
} else if (type === "breakfast") {
return `Good morning. Breakfast is ${veg} and ${carbs}. Yum!`;
} else {
return "No soup for you!";
}
}
export default function getMeal() {
const meal = formatMeal();
return meal;
}
Run Code Online (Sandbox Code Playgroud)
我有以下测试:
__tests __/meal_test.js
import getMeal from "../src/meal";
describe("meal tests", () => {
beforeEach(() => {
jest.resetModules();
});
it("should print dinner", () => {
expect(getMeal()).toBe(
"Good evening. Dinner is green beans and rice. Yum!"
);
});
it("should print breakfast (mocked)", () => {
jest.doMock("../src/food", () => ({
type: "breakfast",
veg: "avocado",
carbs: "toast"
}));
// prints out the newly mocked food!
console.log(require("../src/food"));
// ...but we didn't mock it in time, so this fails!
expect(getMeal()).toBe("Good morning. Breakfast is avocado and toast. Yum!");
});
});
Run Code Online (Sandbox Code Playgroud)
我如何正确地模拟Food 每次测试?换句话说,我只想将模拟应用于"should print breakfast (mocked)"测试用例.
我也不想理想地更改应用程序源代码(虽然可能让Food成为一个返回一个对象的函数而不是可接受的 - 但仍然无法让它工作.)
我已经尝试过的事情:
Food通过getMeal+使用依赖注入将对象线程化formatMeal
Food绕过整个应用程序)jest.mock()- 这可能是答案在某处,但由于导入时间怪异,在这里控制值并在每次测试时重置它很困难
jest.mock()在顶部使用将覆盖每个测试用例,并且我无法确定如何更改或重置Food每个测试的值.谢谢!
使用require抢在每个测试功能的新模块后设立嘲笑.
it("should print breakfast (mocked)", () => {
jest.doMock(...);
const getMeal = require("../src/meal").default;
...
});
Run Code Online (Sandbox Code Playgroud)
要么
转Food成一个功能,把调用jest.mock到模块范围.
import getMeal from "../src/meal";
import food from "../src/food";
jest.mock("../src/food");
food.mockReturnValue({ ... });
...
Run Code Online (Sandbox Code Playgroud)
Jest 手册中有一个片段,内容如下:
注意:为了正确模拟,Jest需要jest.mock('moduleName')与require/import语句在同一范围内.
在相同的手册还规定:
如果您正在使用ES模块导入,那么您通常倾向于将导入语句放在测试文件的顶部.但是通常你需要指示Jest在模块使用它之前使用模拟.出于这个原因,Jest会自动将jest.mock调用提升到模块的顶部(在任何导入之前).
在执行任何测试功能之前,ES6导入在模块范围内得到解析.因此,对于要应用的模拟,需要在导入任何模块之前在测试函数之外声明它们.Jest的Babel插件将"提升" jest.mock语句到文件的开头,以便在进行任何导入之前执行它们.请注意,jest.doMock是故意不悬挂.
可以通过查看Jest的缓存目录(运行jest --showConfig以了解位置)来研究生成的代码.
food示例中的模块很难模拟,因为它是一个对象文字,而不是一个函数.最简单的方法是每次需要更改值时强制重新加载模块.
ES6 import语句必须是模块作用域,但"good old" require没有这样的限制,可以从测试方法的范围调用.
describe("meal tests", () => {
beforeEach(() => {
jest.resetModules();
});
it("should print dinner", () => {
const getMeal = require("../src/meal").default;
expect(getMeal()).toBe(
"Good evening. Dinner is green beans and rice. Yum!"
);
});
it("should print breakfast (mocked)", () => {
jest.doMock("../src/food", () => ({
type: "breakfast",
veg: "avocado",
carbs: "toast"
}));
const getMeal = require("../src/meal").default;
// ...this works now
expect(getMeal()).toBe("Good morning. Breakfast is avocado and toast. Yum!");
});
});
Run Code Online (Sandbox Code Playgroud)
也可以包装被测功能.
代替
import getMeal from "../src/meal";
Run Code Online (Sandbox Code Playgroud)
使用
const getMeal = () => require("../src/meal").default();
Run Code Online (Sandbox Code Playgroud)
如果food模块暴露了函数而不是文字,则可以模拟它.模拟实例是可变的,可以从测试更改为测试.
SRC/food.js
const Food = {
carbs: "rice",
veg: "green beans",
type: "dinner"
};
export default function() { return Food; }
Run Code Online (Sandbox Code Playgroud)
SRC/meal.js
import getFood from "./food";
function formatMeal() {
const { carbs, veg, type } = getFood();
if (type === "dinner") {
return `Good evening. Dinner is ${veg} and ${carbs}. Yum!`;
} else if (type === "breakfast") {
return `Good morning. Breakfast is ${veg} and ${carbs}. Yum!`;
} else {
return "No soup for you!";
}
}
export default function getMeal() {
const meal = formatMeal();
return meal;
}
Run Code Online (Sandbox Code Playgroud)
__tests __/meal_test.js
import getMeal from "../src/meal";
import food from "../src/food";
jest.mock("../src/food");
const realFood = jest.requireActual("../src/food").default;
food.mockImplementation(realFood);
describe("meal tests", () => {
beforeEach(() => {
jest.resetModules();
});
it("should print dinner", () => {
expect(getMeal()).toBe(
"Good evening. Dinner is green beans and rice. Yum!"
);
});
it("should print breakfast (mocked)", () => {
food.mockReturnValueOnce({
type: "breakfast",
veg: "avocado",
carbs: "toast"
});
// ...this works now
expect(getMeal()).toBe("Good morning. Breakfast is avocado and toast. Yum!");
});
});
Run Code Online (Sandbox Code Playgroud)
当然还有其他选项,比如将测试拆分为两个模块,其中一个文件设置模拟,另一个使用真实模块或返回可变对象代替food模块的默认导出,以便每个模块可以修改测试然后手动重置beforeEach.