我如何创建假函数来测试组件?

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 中,如果您导出的函数 getAvailableBillssrc/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。您也可以在需要时使用 mockResolvedValueOncemockReturnValueOnce 仅模拟一次,或者使用 mockRejectedValue.

模拟被拒绝的承诺

顺便说一句,你最好将测试封装在describe里面,这样可以避免一些错误,并且使测试更可读可写。您还应该在 beforeAll / beforeEach 方法内部进行模拟,以便在需要时为多个测试模拟一次。

如果需要,您还可以添加一个设置文件,在其中实例化 jest.config 中的假 env 值。这不是重点,但有一天它可能会对你有所帮助。

如果您要更换组件的 function/method,简短的回答是:您不需要!

切勿在测试中替换组件的部件。测试用于检查您的组件是否在现在和将来在您对其进行更改后失败。如果您将来引入重大更改,但测试替换了您引入更改的 part/method,您的测试将如何知道应用已损坏?


测试时需要遵循几个重要原则:

  1. 请勿修改您的组件以使其通过测试。仅对其进行修改以修复在测试期间发现的实际问题。
  2. 切勿在测试期间更换组件的部件。你可以(也应该)做的是模拟它外部的任何东西,用 functions/modules 替换那些部分来执行你期望这些部分在实时环境中做的事情 1。模拟的要点是:
    jest.mock('some-module', () => jest.fn().mockImplementation(() => yourMock )).
    完整文档 here.
    模拟需要在您的测试调用 import Something from 'some-module'.
  3. 之前发生
  4. 仅测试 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 时按预期运行。