【问题标题】:Cannot mock filesystem in nodejs unit tests无法在 nodejs 单元测试中模拟文件系统
【发布时间】:2018-08-07 01:57:14
【问题描述】:

概述

我有一个用 nodejs 编写的简单模块,它使用 fs-extra 包来测试文件是否存在。当路径存在时模块抛出,否则继续下一个过程。这是源文件:

// - main.js -
import fs from 'fs-extra'

export default async (pathName) => {
  // Do not proceed if path already exists.
  if (await fs.pathExists(projectPath)) {
    throw new Error(chalk.red.bold(`${projectPath} already exists`))
  }

  // more logic here
}

我想写一个单元测试来测试下面的逻辑:

  • 如果文件路径存在,我们预计会抛出错误

我不想弄乱真正的文件系统——以防我的代码包含一些可能破坏它的讨厌的错误——所以我采用了另一种解决方案,使用mock-fs 模拟文件系统。这是规范文件:

// - main.js spec file -
import mainFunction from '../main'
import mockfs from 'mock-fs'

describe('test main function', () => {
  beforeEach(() => {
    mockfs({
      home: {
        user: {
          dummy: {}
        }
      }
    })
  })

  test('expect to throw', async () => {
    await mainFunction('/home/user/dummy')
  })

  afterEach(() => {
    mockfs.restore()
  })
})

有什么问题?

每次我运行测试时,主函数都不会抛出。发生这种情况是因为在规范文件中声明了mockfs fake-filesystem,因此主源文件中的fs 模块不知道mockfs fake-filesystem 并检查真实的。当我的真实文件系统中没有名为 /home/user/dummy 的文件夹时,检查总是失败。

预期行为

spec 文件中的mainFunction 应该抛出

实际行为

spec 文件中的mainFunction 不会抛出

其他信息

我想我可以把这个单元测试变成一个集成测试。但我不想。有什么解决办法吗?我必须使用其他软件包吗? 我的测试服是Jest 22.3.0。

【问题讨论】:

  • 你为什么不测试一些保证不存在的随机路径?喜欢/does/not/exist。我很确定这不会破坏您的文件系统,因为您依赖于 fs-extra 模块,所以只要该模块不破坏文件系统,您就可以使用了。
  • @devius 感谢您的回复。这样做的意思是找到一种方法来通过单元测试而不是 e2e 来做到这一点。我知道您的回答是一个不错的选择,但我想知道是否有其他方法可以做到这一点。
  • 嗯....它非常复杂,但是您可以修改requires缓存中的fs-extra模块以指向您的mock-fs模块,然后在测试完成后重新加载正确的fs-extra 模块。但这似乎太麻烦了,因为害怕损坏文件系统,或者试图将“单元”测试非常严格。也许我应该添加这个作为答案。
  • @devius 感谢您的 cmets。我找到了解决方案。见下文。

标签: node.js unit-testing mocking fs


【解决方案1】:

经过一番搜索,我找到了对分支进行单元测试的合适方法。我们真的不必使用mock-fs 模块。我们只需要模拟fs-extra 模块的pathExists 方法以返回一次值false 和一次值true。贝娄,我发布了我的规范文件的工作版本:

import mainFunction from '../main'

require('fs-extra').pathExists = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)

describe('test main function', () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

  test('expect to not throw', async () => {
    await expect(mainFunction('/dummy/path/does/not/matter')).resolves
  })

  test('expect to throw', async () => {
    await expect(mainFunction('/dummy/path/does/not/matter')).rejects.toBeInstanceOf(Error)
  })
})

【讨论】:

  • 它正在工作,因为您已经处理了承诺拒绝。您可以通过删除承诺拒绝来重新运行它并确认它是否仍然有效吗?
猜你喜欢
  • 2013-11-09
  • 2011-10-04
  • 2010-11-08
  • 1970-01-01
  • 2015-02-27
  • 2020-02-13
  • 2019-11-27
  • 1970-01-01
相关资源
最近更新 更多