监视导入/模拟导入



我正在VueJS应用程序上使用vitest编写单元测试。

作为应用程序的一部分,我们有一组API包装服务,例如users.js,它包装了我们的相关API调用以检索用户信息:

import client from './client'
const getUsers = () => {
return client.get(...)
}
export default {
getUsers
}

这些服务中的每一个利用公共CCD_ 2,该公共CCD_;拦截器管理。

对于我们的单元测试,我想检查是否调用了相关的url,因此希望监视或模拟client

我已经学习了各种例子和文章,但很难弄清楚如何模拟导入(users.js)的导入(client)。

我能得到的最接近的(基于这些帖子-1,2)是:

import { expect, vi } from 'vitest'
import * as client from '<path/to/client.js>'
import UsersAPI from '<path/to/users.js>'
describe('Users API', () => {
beforeEach(() => {
const spy = vi.spyOn(client, 'default')    // mock a named export
expect(spy).toHaveBeenCalled() // client is called at the top of users.js
})
test('Users API.getUsers', () => {
UsersAPI.getUsers()
expect(spy).toHaveBeenCalled()
})
})

但它正在启动:

❯ async frontend/src/api/client.js:3:31
2| import store from '@/store'
3| 
4| const client = axios.create({
|                              ^
5|     headers: {
6|         'Content-Type': 'application/json'

它仍在尝试加载真正的CCD_ 8文件。

我似乎不能显式地模拟client,因为import语句首先运行,所以client在我修改/拦截它之前被导入到users.js

vi.mock('client', () => {
return {
default: {
get: vi.fn()
}
}
})

模拟模块

vi.mock()的path参数需要解析为被测模块正在使用的同一文件。如果users.js导入<root>/src/client.js,则vi.mock()的路径参数需要匹配:

// users.js
import client from './client' // => resolves to path/to/client.js
// users.spec.js
vi.mock('../../client.js')    // => resolves to path/to/client.js

在这里使用路径别名通常会有所帮助。

监视/嘲笑函数

要监视或模拟模拟模块的功能,请在test()中执行以下操作:

  1. 动态导入模块,得到模拟模块
  2. 从模拟的模块引用中模拟函数,可以选择返回一个模拟值。由于client.get()返回client.js0,而axios.get()返回Promise,因此使用mockResolvedValue()模拟返回的数据是有意义的
// users.spec.js
import { describe, test, expect, vi } from 'vitest'
import UsersAPI from '@/users.js'
vi.mock('@/client')
describe('Users API', () => {
test('Users API.getUsers', async () => {
1️⃣
const client = await import('@/client')
2️⃣
const response = { data: [{ id: 1, name: 'john doe' }] }
client.default.get = vi.fn().mockResolvedValue(response)

const users = await UsersAPI.getUsers()
expect(client.default.get).toHaveBeenCalled()
expect(users).toEqual(response)
})
})

演示

派对迟到了,但以防万一其他人也面临同样的问题。

我通过在测试文件中导入模块依赖项并首先模拟整个模块,然后只模拟我需要的方法来解决这个问题。

import { client } from 'client';
vi.mock('client', () => {
const client = vi.fn();
client.get = vi.fn();

return { client }
});

然后,在那些在后台调用client.get()作为依赖项的测试中,只需添加

client.get.mockResolvedValue({fakeResponse: []});

并且将调用模拟函数而不是实际实现。

如果您使用默认导出,请查看vitest文档,因为您需要提供默认密钥。

如果使用默认导出来模拟模块,则需要在返回的工厂函数对象中提供默认键。这是一个特定于ES模块的警告,因此jest文档可能会有所不同,因为jest使用的是commonJS模块。

我已经接受了上面的答案,因为这确实解决了我最初的问题,但也希望包括我需要的额外步骤。

在我的用例中,我需要模拟整个模块导入,因为我在API文件上有一组级联的导入,这些导入反过来又导入了越来越多的依赖项。

为了解决这个问题,我在vuex文档中发现了这个关于嘲笑行为的内容:

https://vuex.vuejs.org/guide/testing.html#testing-行动

详细介绍了使用webpack和inject-loader用mock替换整个模块,从而完全防止源文件加载。

最新更新