JavaScript测试-具有相同玩笑的来自同一模块的模拟功能

时间:2018-10-02 14:38:24

标签: javascript unit-testing webpack jestjs

一段时间以来,我一直在尝试寻找此问题的答案,但失败了。所以我决定在这里尝试一下。如果已经有这样的问题,但我错过了,对不起,重复一遍。

假设我有这个javascript模块'myValidator.js',其中有两个函数,一个函数调用另一个。

export const validate = (value) => {
  if (!value.something) {
    return false
  }

  // other tests like that
  return true
}

export const processValue = (value) => {
  if (!validate(value)) {
    return null
  }

  // do some stuff with value and return something
}

像这样测试。 我想测试验证功能,是否行为正确。然后,我有了processValue函数,该函数调用第一个函数,并在验证正常或为空时返回一些值。

import * as myValidator from './myValidator'

describe('myValidator', () => {
  describe('validate', () => {
    it('should return false when something not defined', () => {
      ...
    }
  }

  describe('processValue', () => {
    it('should return something when value is valid', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => true)
      expect(validate('something')).toEqual('somethingProcessed')
    }

    it('should return null when validation fails', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => false)
      expect(validate('somethingElse')).toEqual(null)
    }
  }
}

现在,问题在于这实际上不起作用,因为processValue()实际上在模块内部调用了该函数,因为我想是闭包了。因此,该函数不会被模拟,因为我认为只有导出中的引用已更改为开玩笑的模拟。

我已经找到了解决方法,并且在模块内部可以使用

if (!exports.validate(value)) 

适用于测试。但是,我们使用Webpack(v4)来构建应用程序,因此它将导出的内容转换为自己的结构,然后在启动应用程序时未定义exports并且代码失败。

什么是最好的测试方法?

对于这种简单的情况,我可以通过提供一些有效和无效的值来做到这一点,但是我认为应该单独进行测试。

还是最好不要模拟函数并通过调用来避免出现我的问题,还是有某种方法可以使用JavaScript模块实现这一目标?

1 个答案:

答案 0 :(得分:1)

我终于找到了这个问题的答案。它实际上在Jest examples project on GitHub中。

// Copyright 2004-present Facebook. All Rights Reserved.

/**
 * This file illustrates how to do a partial mock where a subset
 * of a module's exports have been mocked and the rest
 * keep their actual implementation.
 */
import defaultExport, {apple, strawberry} from '../fruit';

jest.mock('../fruit', () => {
  const originalModule = jest.requireActual('../fruit');
  const mockedModule = jest.genMockFromModule('../fruit');

  //Mock the default export and named export 'apple'.
  return {
    ...mockedModule,
    ...originalModule,
    apple: 'mocked apple',
    default: jest.fn(() => 'mocked fruit'),
  };
});

it('does a partial mock', () => {
  const defaultExportResult = defaultExport();
  expect(defaultExportResult).toBe('mocked fruit');
  expect(defaultExport).toHaveBeenCalled();

  expect(apple).toBe('mocked apple');
  expect(strawberry()).toBe('strawberry');
});