繁体   English   中英

监视/模拟导入的导入

[英]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 )。

我能够得到的最接近(基于这些帖子 - 12 )是:

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中。 我对 mocking 的尝试如下(放置在 imports 和describe之间):

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

模拟一个模块

vi.mock()的路径参数需要解析为被测模块正在使用的同一文件。 如果users.js导入<root>/src/client.jsvi.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()返回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)
  })
})

演示

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

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

为了解决这个问题,我在关于模拟操作的 vuex 文档中找到了这个:

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

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

聚会迟到了,但以防万一其他人面临这个问题。

我通过在测试文件和 mocking 首先导入整个模块中的模块依赖关系来解决它,然后只是我需要的方法。


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

然后在测试中调用client.get()在幕后作为依赖项只需添加

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

并且调用了模拟的 function 而不是真正的实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM