我如何创建假函数来测试组件?
How can i create fake functions to test the components?
我必须测试我的组件,但是那个组件有一个功能可以工作,这个功能需要一个关键环境,
我的函数有一个 onMounted 函数
onMounted(async () => {
const available = await getAvailableBills()
实现是:
export const getAvailableBills = async () => {
try {
const bill = `${process.env.VAR_ENV}`
我收到这个错误:
错误:提供的地址 {VAR_ENV} 无效
但我认为我不需要执行真正的函数我想在我的测试中创建假函数
import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'
const store = createStore({
state() {
return {
user: {},
}
},
mutations: {},
})
let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
wrapper = shallowMount(App, {
propsData: {
whitelist: [1,2,3],
},
global: {
plugins: [store],
},
})
})
describe('Tables', () => {
it('test 1', () => {
expect(wrapper.props().list).toEqual([1,2,3])
})
})
你必须嘲笑它。好像你在开玩笑。您可以使用 jest.spyOn
方法模拟特定对象,或使用 jest.mock
.
模拟整个文件
例如,如果您有
src /
- app.js
- app.spec.js
- services /
--- bills.js
在您的 src/app.spec.js
中,如果您导出的函数 getAvailableBills
在 src/services/bills.js
中,只需执行以下操作:
import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'
import { getAvailableBills } from './services/bills'
jest.mock('./services/bills', {
getAvailableBills: jest.fn().mockResolvedValue({ bills: ['...'] }) // you can mock the response directly here for the whole file
})
getAvailableBills.mockResolvedValue({ bills: ['...'] }) // or you can mock the response when you need to mock it
const store = createStore({
state() {
return {
user: {},
}
},
mutations: {},
})
let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
wrapper = shallowMount(App, {
propsData: {
whitelist: [1,2,3],
},
global: {
plugins: [store],
},
})
})
describe('Tables', () => {
it('test 1', () => {
expect(wrapper.props().list).toEqual([1,2,3])
})
})
请注意,我使用 mockResolvedValue
是因为它返回一个 Promise(异步方法),但如果它 returns 是一个直接值而不是一个 promise,请使用 mockReturnValue
。您也可以在需要时使用 mockResolvedValueOnce
和 mockReturnValueOnce
仅模拟一次,或者使用 mockRejectedValue
.
模拟被拒绝的承诺
顺便说一句,你最好将测试封装在describe里面,这样可以避免一些错误,并且使测试更可读可写。您还应该在 beforeAll
/ beforeEach
方法内部进行模拟,以便在需要时为多个测试模拟一次。
如果需要,您还可以添加一个设置文件,在其中实例化 jest.config 中的假 env 值。这不是重点,但有一天它可能会对你有所帮助。
如果您要更换组件的 function/method,简短的回答是:您不需要!。
切勿在测试中替换组件的部件。测试用于检查您的组件是否在现在和将来在您对其进行更改后失败。如果您将来引入重大更改,但测试替换了您引入更改的 part/method,您的测试将如何知道应用已损坏?
测试时需要遵循几个重要原则:
- 请勿修改您的组件以使其通过测试。仅对其进行修改以修复在测试期间发现的实际问题。
- 切勿在测试期间更换组件的部件。你可以(也应该)做的是模拟它外部的任何东西,用 functions/modules 替换那些部分来执行你期望这些部分在实时环境中做的事情 1。模拟的要点是:
jest.mock('some-module', () => jest.fn().mockImplementation(() => yourMock ))
.
完整文档 here.
模拟需要在您的测试调用 import Something from 'some-module'
. 之前发生
- 仅测试 inputs/outputs 您的组件。不要测试你的组件是如何工作的(a.k.a 内部实现)(这允许你在以后的某个时候重构它而不会破坏测试,只要你的组件仍然按预期为每个提供的输入输出)。
也不要测试外部的东西是否有效。您的测试与此无关。外部的东西有自己的测试,而且通常已经过测试。这就是您依赖它的原因。
因此,在您的情况下,您希望在测试期间修改 process.env
。唯一的问题是 process.env
是全局的,如果您对其进行更改,这些更改将在测试结束后持续存在,除非您将所有内容更改回开始测试时的状态。
最重要的是,您必须在每次测试之前调用 jest.resetModules()
,以清除笑话缓存(默认情况下,笑话缓存,因此它运行测试速度更快)。
这是一个如何使用受控 process.env
进行测试的示例。这应该消除了替换组件方法的需要:
describe('SomeComponent', () => {
const REAL_ENV = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...process.env }; // copy, for your test
});
afterAll(() => {
process.env = REAL_ENV; // restore initial values
});
it('should do stuff depending on process.env', () => {
process.env.FOO = 'bar';
wrapper = shallowMount(/*...*/);
expect(/* wrapper to do stuff...*/)
})
});
使用受控 process.env
值进行测试需要在测试内部进行浅安装,而不是 beforeAll
中典型的浅安装,我通常创建两个单独的测试套件:一个用于正常测试,浅安装在beforeAll
,还有一个用于特殊测试,在浅安装之前设置环境变量:
describe('SomeComponent - normal tests', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(/*...*/)
});
it('should do stuff', () => {
// component here has already been shallowMounted
})
});
describe('SomeCompoent - process.env tests', () => {
const REAL_ENV = process.env;
let wrapper;
beforeEach(/* ... */); // replace process.env
afterEach(/* ... */); // restore process.env
it('Does stuff with FOO = "bar"', () => {
process.env.FOO = 'bar';
wrapper = shallowMount(/* ... */);
expect(/* wrapper to do stuff when process.env.FOO is 'bar' */)
})
it('Does stuff with FOO = "baz"', () => {
process.env.FOO = 'baz';
wrapper = shallowMount(/* ... */);
expect(/* wrapper to do stuff when process.env.FOO is 'baz' */)
})
})
1 例如,您不想在测试中使用 axios。您不想测试本地计算机是否已连接到 Internet,或者您的应用程序调用的服务器是否实际启动并且 运行。您想要一个 axios 的替代品,它可以在不进行实际调用的情况下执行 axios 正在做的事情,从而允许您控制返回的响应和错误,从而测试您的组件是否做出正确的调用并在从 axios 接收 responses/errors 时按预期运行。
我必须测试我的组件,但是那个组件有一个功能可以工作,这个功能需要一个关键环境,
我的函数有一个 onMounted 函数
onMounted(async () => {
const available = await getAvailableBills()
实现是:
export const getAvailableBills = async () => {
try {
const bill = `${process.env.VAR_ENV}`
我收到这个错误:
错误:提供的地址 {VAR_ENV} 无效
但我认为我不需要执行真正的函数我想在我的测试中创建假函数
import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'
const store = createStore({
state() {
return {
user: {},
}
},
mutations: {},
})
let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
wrapper = shallowMount(App, {
propsData: {
whitelist: [1,2,3],
},
global: {
plugins: [store],
},
})
})
describe('Tables', () => {
it('test 1', () => {
expect(wrapper.props().list).toEqual([1,2,3])
})
})
你必须嘲笑它。好像你在开玩笑。您可以使用 jest.spyOn
方法模拟特定对象,或使用 jest.mock
.
例如,如果您有
src /
- app.js
- app.spec.js
- services /
--- bills.js
在您的 src/app.spec.js
中,如果您导出的函数 getAvailableBills
在 src/services/bills.js
中,只需执行以下操作:
import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'
import { getAvailableBills } from './services/bills'
jest.mock('./services/bills', {
getAvailableBills: jest.fn().mockResolvedValue({ bills: ['...'] }) // you can mock the response directly here for the whole file
})
getAvailableBills.mockResolvedValue({ bills: ['...'] }) // or you can mock the response when you need to mock it
const store = createStore({
state() {
return {
user: {},
}
},
mutations: {},
})
let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
wrapper = shallowMount(App, {
propsData: {
whitelist: [1,2,3],
},
global: {
plugins: [store],
},
})
})
describe('Tables', () => {
it('test 1', () => {
expect(wrapper.props().list).toEqual([1,2,3])
})
})
请注意,我使用 mockResolvedValue
是因为它返回一个 Promise(异步方法),但如果它 returns 是一个直接值而不是一个 promise,请使用 mockReturnValue
。您也可以在需要时使用 mockResolvedValueOnce
和 mockReturnValueOnce
仅模拟一次,或者使用 mockRejectedValue
.
顺便说一句,你最好将测试封装在describe里面,这样可以避免一些错误,并且使测试更可读可写。您还应该在 beforeAll
/ beforeEach
方法内部进行模拟,以便在需要时为多个测试模拟一次。
如果需要,您还可以添加一个设置文件,在其中实例化 jest.config 中的假 env 值。这不是重点,但有一天它可能会对你有所帮助。
如果您要更换组件的 function/method,简短的回答是:您不需要!。
切勿在测试中替换组件的部件。测试用于检查您的组件是否在现在和将来在您对其进行更改后失败。如果您将来引入重大更改,但测试替换了您引入更改的 part/method,您的测试将如何知道应用已损坏?
测试时需要遵循几个重要原则:
- 请勿修改您的组件以使其通过测试。仅对其进行修改以修复在测试期间发现的实际问题。
- 切勿在测试期间更换组件的部件。你可以(也应该)做的是模拟它外部的任何东西,用 functions/modules 替换那些部分来执行你期望这些部分在实时环境中做的事情 1。模拟的要点是:
jest.mock('some-module', () => jest.fn().mockImplementation(() => yourMock ))
.
完整文档 here.
模拟需要在您的测试调用import Something from 'some-module'
. 之前发生
- 仅测试 inputs/outputs 您的组件。不要测试你的组件是如何工作的(a.k.a 内部实现)(这允许你在以后的某个时候重构它而不会破坏测试,只要你的组件仍然按预期为每个提供的输入输出)。
也不要测试外部的东西是否有效。您的测试与此无关。外部的东西有自己的测试,而且通常已经过测试。这就是您依赖它的原因。
因此,在您的情况下,您希望在测试期间修改 process.env
。唯一的问题是 process.env
是全局的,如果您对其进行更改,这些更改将在测试结束后持续存在,除非您将所有内容更改回开始测试时的状态。
最重要的是,您必须在每次测试之前调用 jest.resetModules()
,以清除笑话缓存(默认情况下,笑话缓存,因此它运行测试速度更快)。
这是一个如何使用受控 process.env
进行测试的示例。这应该消除了替换组件方法的需要:
describe('SomeComponent', () => {
const REAL_ENV = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...process.env }; // copy, for your test
});
afterAll(() => {
process.env = REAL_ENV; // restore initial values
});
it('should do stuff depending on process.env', () => {
process.env.FOO = 'bar';
wrapper = shallowMount(/*...*/);
expect(/* wrapper to do stuff...*/)
})
});
使用受控 process.env
值进行测试需要在测试内部进行浅安装,而不是 beforeAll
中典型的浅安装,我通常创建两个单独的测试套件:一个用于正常测试,浅安装在beforeAll
,还有一个用于特殊测试,在浅安装之前设置环境变量:
describe('SomeComponent - normal tests', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(/*...*/)
});
it('should do stuff', () => {
// component here has already been shallowMounted
})
});
describe('SomeCompoent - process.env tests', () => {
const REAL_ENV = process.env;
let wrapper;
beforeEach(/* ... */); // replace process.env
afterEach(/* ... */); // restore process.env
it('Does stuff with FOO = "bar"', () => {
process.env.FOO = 'bar';
wrapper = shallowMount(/* ... */);
expect(/* wrapper to do stuff when process.env.FOO is 'bar' */)
})
it('Does stuff with FOO = "baz"', () => {
process.env.FOO = 'baz';
wrapper = shallowMount(/* ... */);
expect(/* wrapper to do stuff when process.env.FOO is 'baz' */)
})
})
1 例如,您不想在测试中使用 axios。您不想测试本地计算机是否已连接到 Internet,或者您的应用程序调用的服务器是否实际启动并且 运行。您想要一个 axios 的替代品,它可以在不进行实际调用的情况下执行 axios 正在做的事情,从而允许您控制返回的响应和错误,从而测试您的组件是否做出正确的调用并在从 axios 接收 responses/errors 时按预期运行。