间谍 On/Mocking 进口进口
Spying On/Mocking Import of an Import
我正在 VueJS 应用程序上使用 vitest 编写单元测试。
作为我们应用程序的一部分,我们有一组 API 包装器服务,例如users.js
包装了我们相关的 API 检索用户信息的调用:
import client from './client'
const getUsers = () => {
return client.get(...)
}
export default {
getUsers
}
这些服务中的每一个都使用一个通用的 client.js
,后者又使用 axios
进行 REST 调用和拦截器管理。
对于我们的单元测试,我想检查是否调用了相关的 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'
它仍在尝试加载真正的 client.js
文件。
我似乎无法显式地模拟 client
,因为 import
语句首先是 运行,所以 client
在我之前导入 users.js
可以modify/intercept吗。我的模拟尝试如下(位于导入和 describe
之间):
vi.mock('client', () => {
return {
default: {
get: vi.fn()
}
}
})
模拟一个模块
vi.mock()
的路径参数需要解析为被测模块正在使用的同一个文件。如果 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
在这里使用 path aliases 通常会有帮助。
Spying/mocking一个函数
要监视或模拟模拟模块的功能,请在 test()
中执行以下操作:
- 动态导入模块,获取模拟模块。
- 模拟模拟模块引用的函数,可选择返回模拟值。由于
client.get()
returns axios.get()
,其中 returns 一个 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)
})
})
我接受了上述答案,因为它确实解决了我最初的问题,但也想包括我需要的这个额外步骤。
在我的用例中,我需要模拟整个模块导入,因为我在 API 文件上有一组级联导入,这些文件本身又导入了越来越多的依赖项。
为了解决这个问题,我在关于模拟操作的 vuex 文档中找到了这个:
https://vuex.vuejs.org/guide/testing.html#testing-actions
其中详细介绍了 webpack 的使用和 inject-loader 用 mock 替换整个模块,从而完全阻止源文件加载。
我正在 VueJS 应用程序上使用 vitest 编写单元测试。
作为我们应用程序的一部分,我们有一组 API 包装器服务,例如users.js
包装了我们相关的 API 检索用户信息的调用:
import client from './client'
const getUsers = () => {
return client.get(...)
}
export default {
getUsers
}
这些服务中的每一个都使用一个通用的 client.js
,后者又使用 axios
进行 REST 调用和拦截器管理。
对于我们的单元测试,我想检查是否调用了相关的 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'
它仍在尝试加载真正的 client.js
文件。
我似乎无法显式地模拟 client
,因为 import
语句首先是 运行,所以 client
在我之前导入 users.js
可以modify/intercept吗。我的模拟尝试如下(位于导入和 describe
之间):
vi.mock('client', () => {
return {
default: {
get: vi.fn()
}
}
})
模拟一个模块
vi.mock()
的路径参数需要解析为被测模块正在使用的同一个文件。如果 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
在这里使用 path aliases 通常会有帮助。
Spying/mocking一个函数
要监视或模拟模拟模块的功能,请在 test()
中执行以下操作:
- 动态导入模块,获取模拟模块。
- 模拟模拟模块引用的函数,可选择返回模拟值。由于
client.get()
returnsaxios.get()
,其中 returns 一个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)
})
})
我接受了上述答案,因为它确实解决了我最初的问题,但也想包括我需要的这个额外步骤。
在我的用例中,我需要模拟整个模块导入,因为我在 API 文件上有一组级联导入,这些文件本身又导入了越来越多的依赖项。
为了解决这个问题,我在关于模拟操作的 vuex 文档中找到了这个:
https://vuex.vuejs.org/guide/testing.html#testing-actions
其中详细介绍了 webpack 的使用和 inject-loader 用 mock 替换整个模块,从而完全阻止源文件加载。